{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 202,
   "id": "40cb14e5-d447-42d0-a8c7-bb8d7cd1373d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "D:/BaiduNetdiskDownload/U1_G1_N30_L_L1_D0_20200408_2_Labeled.csv\n",
      "[0 1]\n",
      "D:/BaiduNetdiskDownload/U1_G2_N30_L_L1_D0_20200408_2_Labeled.csv\n",
      "[0 2]\n",
      "D:/BaiduNetdiskDownload/U1_G3_N30_L_L1_D0_20200408_2_Labeled.csv\n",
      "[0 3]\n",
      "D:/BaiduNetdiskDownload/U1_G1_N10_L_L1_D0_20200408_1_Labeled.csv\n",
      "[0 1]\n",
      "D:/BaiduNetdiskDownload/U1_G2_N10_L_L1_D0_20200408_1_Labeled.csv\n",
      "[0 2]\n",
      "D:/BaiduNetdiskDownload/U1_G3_N10_L_L1_D0_20200408_1_Labeled.csv\n",
      "[0 3]\n",
      "Training segments: 183\n",
      "Training Segment 1: 6414\n",
      "Training Segment 2: 3504\n",
      "Training Segment 3: 1214\n",
      "Training Segment 4: 2960\n",
      "Training Segment 5: 974\n",
      "Training Segment 6: 3024\n",
      "Training Segment 7: 814\n",
      "Training Segment 8: 3248\n",
      "Training Segment 9: 734\n",
      "Training Segment 10: 2928\n",
      "Training Segment 11: 782\n",
      "Training Segment 12: 2480\n",
      "Training Segment 13: 1102\n",
      "Training Segment 14: 2656\n",
      "Training Segment 15: 750\n",
      "Training Segment 16: 2672\n",
      "Training Segment 17: 606\n",
      "Training Segment 18: 3200\n",
      "Training Segment 19: 350\n",
      "Training Segment 20: 3392\n",
      "Training Segment 21: 878\n",
      "Training Segment 22: 3136\n",
      "Training Segment 23: 686\n",
      "Training Segment 24: 3408\n",
      "Training Segment 25: 558\n",
      "Training Segment 26: 3056\n",
      "Training Segment 27: 798\n",
      "Training Segment 28: 3472\n",
      "Training Segment 29: 670\n",
      "Training Segment 30: 3328\n",
      "Training Segment 31: 446\n",
      "Training Segment 32: 3472\n",
      "Training Segment 33: 798\n",
      "Training Segment 34: 2960\n",
      "Training Segment 35: 1438\n",
      "Training Segment 36: 3184\n",
      "Training Segment 37: 1134\n",
      "Training Segment 38: 2736\n",
      "Training Segment 39: 718\n",
      "Training Segment 40: 3200\n",
      "Training Segment 41: 942\n",
      "Training Segment 42: 3168\n",
      "Training Segment 43: 1454\n",
      "Training Segment 44: 2848\n",
      "Training Segment 45: 1342\n",
      "Training Segment 46: 3168\n",
      "Training Segment 47: 910\n",
      "Training Segment 48: 2624\n",
      "Training Segment 49: 1198\n",
      "Training Segment 50: 3024\n",
      "Training Segment 51: 974\n",
      "Training Segment 52: 3088\n",
      "Training Segment 53: 974\n",
      "Training Segment 54: 3200\n",
      "Training Segment 55: 558\n",
      "Training Segment 56: 3408\n",
      "Training Segment 57: 798\n",
      "Training Segment 58: 2928\n",
      "Training Segment 59: 1038\n",
      "Training Segment 60: 3424\n",
      "Training Segment 61: 7663\n",
      "Training Segment 62: 7870\n",
      "Training Segment 63: 3632\n",
      "Training Segment 64: 1502\n",
      "Training Segment 65: 4240\n",
      "Training Segment 66: 1102\n",
      "Training Segment 67: 3392\n",
      "Training Segment 68: 1582\n",
      "Training Segment 69: 4096\n",
      "Training Segment 70: 1822\n",
      "Training Segment 71: 3520\n",
      "Training Segment 72: 1470\n",
      "Training Segment 73: 3936\n",
      "Training Segment 74: 1118\n",
      "Training Segment 75: 4480\n",
      "Training Segment 76: 1246\n",
      "Training Segment 77: 3552\n",
      "Training Segment 78: 1646\n",
      "Training Segment 79: 3824\n",
      "Training Segment 80: 2094\n",
      "Training Segment 81: 3440\n",
      "Training Segment 82: 2270\n",
      "Training Segment 83: 4160\n",
      "Training Segment 84: 1630\n",
      "Training Segment 85: 4096\n",
      "Training Segment 86: 1630\n",
      "Training Segment 87: 4256\n",
      "Training Segment 88: 1774\n",
      "Training Segment 89: 3472\n",
      "Training Segment 90: 2270\n",
      "Training Segment 91: 3600\n",
      "Training Segment 92: 2110\n",
      "Training Segment 93: 3696\n",
      "Training Segment 94: 2190\n",
      "Training Segment 95: 3888\n",
      "Training Segment 96: 1902\n",
      "Training Segment 97: 4208\n",
      "Training Segment 98: 1886\n",
      "Training Segment 99: 4256\n",
      "Training Segment 100: 2030\n",
      "Training Segment 101: 4048\n",
      "Training Segment 102: 2478\n",
      "Training Segment 103: 4000\n",
      "Training Segment 104: 2238\n",
      "Training Segment 105: 4224\n",
      "Training Segment 106: 3022\n",
      "Training Segment 107: 4480\n",
      "Training Segment 108: 1310\n",
      "Training Segment 109: 4208\n",
      "Training Segment 110: 2174\n",
      "Training Segment 111: 4096\n",
      "Training Segment 112: 2238\n",
      "Training Segment 113: 4144\n",
      "Training Segment 114: 2766\n",
      "Training Segment 115: 4832\n",
      "Training Segment 116: 1454\n",
      "Training Segment 117: 4224\n",
      "Training Segment 118: 4526\n",
      "Training Segment 119: 4336\n",
      "Training Segment 120: 3438\n",
      "Training Segment 121: 4464\n",
      "Training Segment 122: 9807\n",
      "Training Segment 123: 17694\n",
      "Training Segment 124: 2512\n",
      "Training Segment 125: 1758\n",
      "Training Segment 126: 2192\n",
      "Training Segment 127: 1150\n",
      "Training Segment 128: 2592\n",
      "Training Segment 129: 1694\n",
      "Training Segment 130: 2176\n",
      "Training Segment 131: 1198\n",
      "Training Segment 132: 2144\n",
      "Training Segment 133: 1710\n",
      "Training Segment 134: 2032\n",
      "Training Segment 135: 2222\n",
      "Training Segment 136: 2256\n",
      "Training Segment 137: 2110\n",
      "Training Segment 138: 1760\n",
      "Training Segment 139: 2718\n",
      "Training Segment 140: 2176\n",
      "Training Segment 141: 2110\n",
      "Training Segment 142: 2112\n",
      "Training Segment 143: 2798\n",
      "Training Segment 144: 2400\n",
      "Training Segment 145: 2558\n",
      "Training Segment 146: 2208\n",
      "Training Segment 147: 2926\n",
      "Training Segment 148: 2496\n",
      "Training Segment 149: 2590\n",
      "Training Segment 150: 2448\n",
      "Training Segment 151: 2526\n",
      "Training Segment 152: 2416\n",
      "Training Segment 153: 3006\n",
      "Training Segment 154: 2320\n",
      "Training Segment 155: 3518\n",
      "Training Segment 156: 2032\n",
      "Training Segment 157: 5406\n",
      "Training Segment 158: 2240\n",
      "Training Segment 159: 5278\n",
      "Training Segment 160: 2304\n",
      "Training Segment 161: 8094\n",
      "Training Segment 162: 2192\n",
      "Training Segment 163: 22366\n",
      "Training Segment 164: 2352\n",
      "Training Segment 165: 1726\n",
      "Training Segment 166: 2768\n",
      "Training Segment 167: 2430\n",
      "Training Segment 168: 2176\n",
      "Training Segment 169: 3166\n",
      "Training Segment 170: 2208\n",
      "Training Segment 171: 3950\n",
      "Training Segment 172: 3360\n",
      "Training Segment 173: 3278\n",
      "Training Segment 174: 2672\n",
      "Training Segment 175: 2558\n",
      "Training Segment 176: 2400\n",
      "Training Segment 177: 3630\n",
      "Training Segment 178: 1712\n",
      "Training Segment 179: 3838\n",
      "Training Segment 180: 2240\n",
      "Training Segment 181: 2606\n",
      "Training Segment 182: 2416\n",
      "Training Segment 183: 8559\n",
      "Testing segments: 63\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "import pandas as pd\n",
    "import os\n",
    "# 定义训练文件和测试文件\n",
    "TrainFiles = [\n",
    "    'D:/BaiduNetdiskDownload/U1_G1_N30_L_L1_D0_20200408_2_Labeled.csv',\n",
    "    'D:/BaiduNetdiskDownload/U1_G2_N30_L_L1_D0_20200408_2_Labeled.csv',\n",
    "    'D:/BaiduNetdiskDownload/U1_G3_N30_L_L1_D0_20200408_2_Labeled.csv']\n",
    "\n",
    "TestFiles = [\n",
    "    'D:/BaiduNetdiskDownload/U1_G1_N10_L_L1_D0_20200408_1_Labeled.csv',\n",
    "    'D:/BaiduNetdiskDownload/U1_G2_N10_L_L1_D0_20200408_1_Labeled.csv',\n",
    "    'D:/BaiduNetdiskDownload/U1_G3_N10_L_L1_D0_20200408_1_Labeled.csv'\n",
    "]\n",
    "\n",
    "# 用于读取和处理CSV文件的函数\n",
    "def read_csv_file(file_path):\n",
    "    print(file_path)  # 打印文件路径\n",
    "    df = pd.read_csv(file_path)\n",
    "    csi = df.iloc[:, 2:].values  # 获取除'timestamp'（时间戳）和'label'（标签）之外的所有列数据\n",
    "    label = df['label'].values  # 标签取值情况：0表示静止，1表示向上，2表示向下，3表示向左，4表示向右\n",
    "    timestamp = df['timestamp'].values\n",
    "    print(np.unique(label))  # 打印标签的唯一值（去重后的值）\n",
    "    return csi, label, timestamp\n",
    "\n",
    "def segment_signals(csi, label, timestamp):\n",
    "    segments = []  # 用于存储数据段\n",
    "    s_label = label[0]  # 初始化数据段的标签（取第一个标签值作为初始值）\n",
    "    s_start = 0  # 初始化数据段的起始索引\n",
    "\n",
    "    for i in range(len(label)):  # 遍历所有标签\n",
    "        if label[i]!= s_label:  # 如果当前标签与当前数据段的标签不同\n",
    "            segments.append((csi[s_start:i - 1], s_label, timestamp[s_start:i - 1]))  # 将当前数据段添加到数据段列表中\n",
    "            s_start = i  # 更新数据段的起始索引\n",
    "            s_label = label[i]  # 更新数据段的标签\n",
    "\n",
    "    segments.append((csi[s_start:], s_label, timestamp[s_start:]))  # 将最后一个数据段添加到数据段列表中\n",
    "    return segments\n",
    "\n",
    "# 定义训练数据段和测试数据段\n",
    "trainSegments = []\n",
    "TestSegments = []\n",
    "\n",
    "# 读取并处理训练文件\n",
    "for file in TrainFiles:\n",
    "    s, y, t = read_csv_file(file)\n",
    "    trainSegments.extend(segment_signals(s, y, t))\n",
    "\n",
    "# 读取并处理测试文件\n",
    "for file in TestFiles:\n",
    "    s, y, t = read_csv_file(file)\n",
    "    TestSegments.extend(segment_signals(s, y, t))\n",
    "\n",
    "# 打印训练数据段的数量\n",
    "print(f\"Training segments: {len(trainSegments)}\")\n",
    "\n",
    "# 打印所有训练数据段的长度\n",
    "for i, (s, y, t) in enumerate(trainSegments):\n",
    "    print(f\"Training Segment {i + 1}: {len(s)}\")\n",
    "\n",
    "# 打印测试数据段的数量\n",
    "print(f\"Testing segments: {len(TestSegments)}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 203,
   "id": "16615c39-c2f7-43ac-83c1-05dd4313aba2",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "from scipy.stats import kurtosis\n",
    "from scipy.stats import skew\n",
    "\n",
    "# Extract features of training segments\n",
    "def extract_features(s):\n",
    "    ''' \n",
    "    Extract features of each segment\n",
    "    features include:\n",
    "    - mean\n",
    "    - std\n",
    "    - max\n",
    "    - min\n",
    "    - median\n",
    "    - kurtosis\n",
    "    - skew\n",
    "    \n",
    "    Input:\n",
    "    s: segment (N*30) in training_segments or testing_segments\n",
    "    \n",
    "    Output:\n",
    "    x: 1-D vector (8*30)\n",
    "    '''\n",
    "    x = []\n",
    "    x.extend(np.mean(s, axis=0))\n",
    "    x.extend(np.std(s, axis=0))\n",
    "    x.extend(np.max(s, axis=0))\n",
    "    x.extend(np.min(s, axis=0))\n",
    "    x.extend(np.median(s, axis=0))\n",
    "    x.extend(kurtosis(s, axis=0))\n",
    "    x.extend(skew(s, axis=0))\n",
    "\n",
    "    return np.array(x)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 206,
   "id": "c3c60d74-2442-4dc4-a280-d71b9c9f2690",
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch\n",
    "from torch.utils.data import DataLoader, TensorDataset\n",
    "\n",
    "def one_hot_collate(batch):\n",
    "    data = torch.stack([item[0] for item in batch])  # 将批次中的每个样本的第一个元素（数据）堆叠成一个张量\n",
    "    labels = torch.tensor([item[1] for item in batch])  # 将批次中的每个样本的第二个元素（标签）转换为张量\n",
    "\n",
    "    Olabels = torch.zeros(labels.size(0), 4)  # 创建一个形状为（标签数量，4）的全零张量，这里假设是4个类别\n",
    "    Olabels.scatter_(1, labels.unsqueeze(1), 1)  # 使用scatter_方法将标签进行独热编码，1表示在维度1上进行操作，根据标签值在相应位置设置为1\n",
    "    return data, Olabels\n",
    "\n",
    "batch_size = 5\n",
    "\n",
    "# 构建训练数据集\n",
    "Train1 = [extract_features(s) for s, _, _ in trainSegments]  # 从训练数据段中提取特征（假设extract_features是自定义的提取特征函数）\n",
    "Train1 = torch.tensor(Train1, dtype=torch.float32)  # 将提取的特征转换为张量，数据类型为float32\n",
    "Train2 = [y for _, y, _ in trainSegments]  # 从训练数据段中提取标签\n",
    "Train2 = torch.tensor(Train2)  # 将标签转换为张量\n",
    "\n",
    "# 构建测试数据集\n",
    "Test1 = [extract_features(s) for s, _, _ in TestSegments]  # 从测试数据段中提取特征\n",
    "Test1 = torch.tensor(Test1, dtype=torch.float32)  # 将提取的特征转换为张量，数据类型为float32\n",
    "Test2 = [y for _, y, _ in TestSegments]  # 从测试数据段中提取标签\n",
    "Test2 = torch.tensor(Test2)  # 将标签转换为张量\n",
    "\n",
    "# 归一化训练数据和测试数据\n",
    "# 从训练数据计算均值和标准差\n",
    "mean = Train1.mean(dim=0)\n",
    "std = Train1.std(dim=0)\n",
    "\n",
    "# 归一化训练数据\n",
    "Train1 = (Train1 - mean) / std\n",
    "\n",
    "# 使用训练数据的均值和标准差归一化测试数据\n",
    "Test1 = (Test1 - mean) / std\n",
    "\n",
    "# 构建数据集\n",
    "TrainDataset = TensorDataset(Train1, Train2)  # 创建训练数据集\n",
    "TestDataset = TensorDataset(Test1, Test2)  # 创建测试数据集\n",
    "\n",
    "# 构建数据加载器\n",
    "TrainLoader = DataLoader(TrainDataset, batch_size=batch_size, shuffle=True, num_workers=0, collate_fn=one_hot_collate)  # 创建训练数据加载器，设置批量大小为4，随机打乱数据，使用0个工作进程，使用one_hot_collate函数处理批次数据\n",
    "TestLoader = DataLoader(TestDataset, batch_size=batch_size, shuffle=False, num_workers=0, collate_fn=one_hot_collate)  # 创建测试数据加载器，不随机打乱数据，其他参数与训练数据加载器类似"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 208,
   "id": "9cbce14a-9920-4e5e-855f-36801fc6c651",
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch.nn as nn\n",
    "\n",
    "class FNN(nn.Module):\n",
    "    def __init__(self, input_size, hidden_size, num_classes):\n",
    "        super(FNN, self).__init__()\n",
    "        self.fc1 = nn.Linear(input_size, hidden_size)\n",
    "        self.relu1 = nn.ReLU()\n",
    "        self.fc2 = nn.Linear(hidden_size, hidden_size)\n",
    "        self.relu2 = nn.ReLU()\n",
    "        self.fc3 = nn.Linear(hidden_size, num_classes)\n",
    "        self.softmax = nn.Softmax(dim=1)\n",
    "    \n",
    "    def forward(self, x):\n",
    "        x = self.fc1(x)\n",
    "        x = self.relu1(x)\n",
    "        x = self.fc2(x)\n",
    "        x = self.relu2(x)\n",
    "        x = self.fc3(x)\n",
    "        out = self.softmax(x)\n",
    "        return out"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 218,
   "id": "123d2cbc-692f-44bf-9029-a7453bc12548",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "FNN(\n",
      "  (fc1): Linear(in_features=210, out_features=15, bias=True)\n",
      "  (relu1): ReLU()\n",
      "  (fc2): Linear(in_features=15, out_features=15, bias=True)\n",
      "  (relu2): ReLU()\n",
      "  (fc3): Linear(in_features=15, out_features=4, bias=True)\n",
      "  (softmax): Softmax(dim=1)\n",
      ")\n",
      "Epoch [1/100], Train Loss: 1.2788, CV Loss: 1.1477\n",
      "Epoch [2/100], Train Loss: 1.1062, CV Loss: 1.0758\n",
      "Epoch [3/100], Train Loss: 1.0679, CV Loss: 1.0770\n",
      "Epoch [4/100], Train Loss: 1.0466, CV Loss: 1.0852\n",
      "Epoch [5/100], Train Loss: 1.0252, CV Loss: 1.0994\n",
      "Epoch [6/100], Train Loss: 1.0050, CV Loss: 1.1031\n",
      "Epoch [7/100], Train Loss: 0.9698, CV Loss: 1.0912\n",
      "Epoch [8/100], Train Loss: 0.9350, CV Loss: 1.0919\n",
      "Epoch [9/100], Train Loss: 0.9164, CV Loss: 1.0818\n",
      "Epoch [10/100], Train Loss: 0.9113, CV Loss: 1.0786\n",
      "Epoch [11/100], Train Loss: 0.8992, CV Loss: 1.0681\n",
      "Epoch [12/100], Train Loss: 0.8873, CV Loss: 1.0496\n",
      "Epoch [13/100], Train Loss: 0.8796, CV Loss: 1.0548\n",
      "Epoch [14/100], Train Loss: 0.8646, CV Loss: 1.0552\n",
      "Epoch [15/100], Train Loss: 0.8494, CV Loss: 1.0432\n",
      "Epoch [16/100], Train Loss: 0.8327, CV Loss: 1.0413\n",
      "Epoch [17/100], Train Loss: 0.8170, CV Loss: 1.0428\n",
      "Epoch [18/100], Train Loss: 0.8061, CV Loss: 1.0459\n",
      "Epoch [19/100], Train Loss: 0.7970, CV Loss: 1.0390\n",
      "Epoch [20/100], Train Loss: 0.7890, CV Loss: 1.0329\n",
      "Epoch [21/100], Train Loss: 0.7836, CV Loss: 1.0310\n",
      "Epoch [22/100], Train Loss: 0.7785, CV Loss: 1.0283\n",
      "Epoch [23/100], Train Loss: 0.7734, CV Loss: 1.0153\n",
      "Epoch [24/100], Train Loss: 0.7695, CV Loss: 1.0127\n",
      "Epoch [25/100], Train Loss: 0.7674, CV Loss: 1.0123\n",
      "Epoch [26/100], Train Loss: 0.7643, CV Loss: 1.0081\n",
      "Epoch [27/100], Train Loss: 0.7622, CV Loss: 1.0024\n",
      "Epoch [28/100], Train Loss: 0.7607, CV Loss: 1.0007\n",
      "Epoch [29/100], Train Loss: 0.7600, CV Loss: 0.9961\n",
      "Epoch [30/100], Train Loss: 0.7589, CV Loss: 0.9993\n",
      "Epoch [31/100], Train Loss: 0.7581, CV Loss: 1.0004\n",
      "Epoch [32/100], Train Loss: 0.7576, CV Loss: 0.9938\n",
      "Epoch [33/100], Train Loss: 0.7572, CV Loss: 0.9940\n",
      "Epoch [34/100], Train Loss: 0.7568, CV Loss: 0.9935\n",
      "Epoch [35/100], Train Loss: 0.7565, CV Loss: 0.9923\n",
      "Epoch [36/100], Train Loss: 0.7563, CV Loss: 0.9907\n",
      "Epoch [37/100], Train Loss: 0.7561, CV Loss: 0.9875\n",
      "Epoch [38/100], Train Loss: 0.7559, CV Loss: 0.9868\n",
      "Epoch [39/100], Train Loss: 0.7557, CV Loss: 0.9876\n",
      "Epoch [40/100], Train Loss: 0.7556, CV Loss: 0.9845\n",
      "Epoch [41/100], Train Loss: 0.7555, CV Loss: 0.9840\n",
      "Epoch [42/100], Train Loss: 0.7554, CV Loss: 0.9819\n",
      "Epoch [43/100], Train Loss: 0.7552, CV Loss: 0.9809\n",
      "Epoch [44/100], Train Loss: 0.7551, CV Loss: 0.9809\n",
      "Epoch [45/100], Train Loss: 0.7551, CV Loss: 0.9795\n",
      "Epoch [46/100], Train Loss: 0.7550, CV Loss: 0.9762\n",
      "Epoch [47/100], Train Loss: 0.7549, CV Loss: 0.9746\n",
      "Epoch [48/100], Train Loss: 0.7581, CV Loss: 0.9684\n",
      "Epoch [49/100], Train Loss: 0.7547, CV Loss: 0.9657\n",
      "Epoch [50/100], Train Loss: 0.7544, CV Loss: 0.9640\n",
      "Epoch [51/100], Train Loss: 0.7546, CV Loss: 0.9645\n",
      "Epoch [52/100], Train Loss: 0.7543, CV Loss: 0.9634\n",
      "Epoch [53/100], Train Loss: 0.7543, CV Loss: 0.9624\n",
      "Epoch [54/100], Train Loss: 0.7543, CV Loss: 0.9641\n",
      "Epoch [55/100], Train Loss: 0.7542, CV Loss: 0.9633\n",
      "Epoch [56/100], Train Loss: 0.7543, CV Loss: 0.9709\n",
      "Epoch [57/100], Train Loss: 0.7544, CV Loss: 0.9662\n",
      "Epoch [58/100], Train Loss: 0.7543, CV Loss: 0.9679\n",
      "Epoch [59/100], Train Loss: 0.7542, CV Loss: 0.9678\n",
      "Epoch [60/100], Train Loss: 0.7543, CV Loss: 0.9660\n",
      "Epoch [61/100], Train Loss: 0.7541, CV Loss: 0.9669\n",
      "Epoch [62/100], Train Loss: 0.7541, CV Loss: 0.9679\n",
      "Epoch [63/100], Train Loss: 0.7542, CV Loss: 0.9690\n",
      "Epoch [64/100], Train Loss: 0.7542, CV Loss: 0.9704\n",
      "Epoch [65/100], Train Loss: 0.7542, CV Loss: 0.9695\n",
      "Epoch [66/100], Train Loss: 0.7541, CV Loss: 0.9664\n",
      "Epoch [67/100], Train Loss: 0.7540, CV Loss: 0.9735\n",
      "Epoch [68/100], Train Loss: 0.7542, CV Loss: 0.9685\n",
      "Epoch [69/100], Train Loss: 0.7540, CV Loss: 0.9699\n",
      "Epoch [70/100], Train Loss: 0.7541, CV Loss: 0.9739\n",
      "Epoch [71/100], Train Loss: 0.7541, CV Loss: 0.9686\n",
      "Epoch [72/100], Train Loss: 0.7540, CV Loss: 0.9677\n",
      "Epoch [73/100], Train Loss: 0.7541, CV Loss: 0.9688\n",
      "Epoch [74/100], Train Loss: 0.7539, CV Loss: 0.9702\n",
      "Epoch [75/100], Train Loss: 0.7539, CV Loss: 0.9707\n",
      "Epoch [76/100], Train Loss: 0.7540, CV Loss: 0.9777\n",
      "Epoch [77/100], Train Loss: 0.7542, CV Loss: 0.9705\n",
      "Epoch [78/100], Train Loss: 0.7539, CV Loss: 0.9761\n",
      "Epoch [79/100], Train Loss: 0.7542, CV Loss: 0.9693\n",
      "Epoch [80/100], Train Loss: 0.7539, CV Loss: 0.9699\n",
      "Epoch [81/100], Train Loss: 0.7541, CV Loss: 0.9731\n",
      "Epoch [82/100], Train Loss: 0.7541, CV Loss: 0.9657\n",
      "Epoch [83/100], Train Loss: 0.7573, CV Loss: 0.9698\n",
      "Epoch [84/100], Train Loss: 0.7543, CV Loss: 0.9914\n",
      "Epoch [85/100], Train Loss: 0.7542, CV Loss: 0.9907\n",
      "Epoch [86/100], Train Loss: 0.7542, CV Loss: 0.9876\n",
      "Epoch [87/100], Train Loss: 0.7541, CV Loss: 0.9812\n",
      "Epoch [88/100], Train Loss: 0.7539, CV Loss: 0.9853\n",
      "Epoch [89/100], Train Loss: 0.7541, CV Loss: 0.9715\n",
      "Epoch [90/100], Train Loss: 0.7539, CV Loss: 0.9863\n",
      "Epoch [91/100], Train Loss: 0.7542, CV Loss: 0.9856\n",
      "Epoch [92/100], Train Loss: 0.7540, CV Loss: 0.9738\n",
      "Epoch [93/100], Train Loss: 0.7539, CV Loss: 0.9844\n",
      "Epoch [94/100], Train Loss: 0.7541, CV Loss: 0.9747\n",
      "Epoch [95/100], Train Loss: 0.7538, CV Loss: 0.9793\n",
      "Epoch [96/100], Train Loss: 0.7541, CV Loss: 0.9712\n",
      "Epoch [97/100], Train Loss: 0.7538, CV Loss: 0.9711\n",
      "Epoch [98/100], Train Loss: 0.7538, CV Loss: 0.9894\n",
      "Epoch [99/100], Train Loss: 0.7541, CV Loss: 0.9848\n",
      "Epoch [100/100], Train Loss: 0.7540, CV Loss: 0.9723\n"
     ]
    }
   ],
   "source": [
    "# 定义模型参数\n",
    "hidden_size = 15\n",
    "\n",
    "# 实例化模型\n",
    "input_size = Train1.shape[1]  # 获取训练数据特征的维度作为输入大小\n",
    "num_classes = 4  # 4个类别（3种动作和静止）\n",
    "FNNmodel = FNN(input_size, hidden_size, num_classes)  # 创建FNN模型实例，传入输入大小、隐藏层大小和类别数量\n",
    "print(FNNmodel)\n",
    "\n",
    "# 定义损失函数和优化器\n",
    "criterion = nn.CrossEntropyLoss()  # 使用交叉熵损失函数\n",
    "optimizer = torch.optim.Adam(FNNmodel.parameters())  # 使用Adam优化器来优化模型参数\n",
    "\n",
    "# 用于存储损失值的列表\n",
    "TrainLosses = []\n",
    "TestLosses = []\n",
    "\n",
    "# 训练的轮数\n",
    "num_epochs = 100\n",
    "\n",
    "for epoch in range(num_epochs):\n",
    "    FNNmodel.train()  # 将模型设置为训练模式\n",
    "    BatchLosses = []  # 用于存储每个批次的损失值\n",
    "\n",
    "    for batchx, batchy in TrainLoader:\n",
    "        # 前向传播\n",
    "        BatchOutputs = FNNmodel(batchx)  # 将批次数据输入模型得到输出\n",
    "        Batchloss = criterion(BatchOutputs, batchy)  # 计算输出与真实标签之间的损失\n",
    "\n",
    "        # 反向传播和优化\n",
    "        optimizer.zero_grad()  # 清空之前的梯度\n",
    "        Batchloss.backward()  # 计算梯度\n",
    "        optimizer.step()  # 根据梯度更新模型参数\n",
    "\n",
    "        BatchLosses.append(Batchloss.item())  # 将当前批次的损失值添加到列表中\n",
    "\n",
    "    # 计算本训练轮次的平均训练损失\n",
    "    AvgTrainLoss = sum(BatchLosses) / len(BatchLosses)\n",
    "    TrainLosses.append(AvgTrainLoss)\n",
    "\n",
    "    # 在交叉验证集（验证集）上评估模型\n",
    "    FNNmodel.eval()  # 将模型设置为评估模式\n",
    "    TestBatchLoss = []\n",
    "    with torch.no_grad():\n",
    "        for testx, testy in TestLoader:\n",
    "            TestOutputs = FNNmodel(testx)  # 在验证集数据上进行前向传播得到输出\n",
    "            TestLoss = criterion(TestOutputs, testy)  # 计算验证集上的损失\n",
    "            TestBatchLoss.append(TestLoss.item())\n",
    "\n",
    "    AvgTestLoss = sum(TestBatchLoss) / len(TestBatchLoss)\n",
    "    TestLosses.append(AvgTestLoss)\n",
    "\n",
    "    print(f'Epoch [{epoch + 1}/{num_epochs}], Train Loss: {AvgTrainLoss:.4f}, CV Loss: {AvgTestLoss:.4f}')  # 打印当前轮次的训练损失和验证损失，保留4位小数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 219,
   "id": "1bfea854-e9a9-48bb-a4de-fdaa25f594a3",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Accuracy on training set: 98.91%\n",
      "Accuracy on cross-validation set: 76.19%\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAA04AAAHCCAYAAADYTZkLAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguNCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8fJSN1AAAACXBIWXMAAA9hAAAPYQGoP6dpAABviUlEQVR4nO3dd3hUVf7H8ffMpIckEAIJJUDoTamighQVEaz87K4CdrEhsmthXeuq2HUVxV1XxQ6KiqiogIUiskgVpUPoCaGmkjZzf3+czCSBkEaSO0k+r+e5z8zcuXPnO8klzOeec89xWJZlISIiIiIiIsfltLsAERERERERf6fgJCIiIiIiUgYFJxERERERkTIoOImIiIiIiJRBwUlERERERKQMCk4iIiIiIiJlUHASEREREREpg4KTiIiIiIhIGRScREREREREyqDgJCIiIiIiUoYAO998wYIFPPfccyxfvpykpCS++OILRo4cedztFy1axP3338/69evJysqidevW3Hrrrdxzzz3lfk+Px8OePXuIiIjA4XBUwacQEREREZHayLIs0tPTad68OU5n6W1KtganzMxMevTowfXXX8+ll15a5vbh4eHceeednHzyyYSHh7No0SJuvfVWwsPDueWWW8r1nnv27CE+Pv5ESxcRERERkTpi586dtGzZstRtHJZlWTVUT6kcDkeZLU4lueSSSwgPD+f9998v1/apqak0bNiQnTt3EhkZWYlKRURERESkLkhLSyM+Pp7Dhw8TFRVV6ra2tjidqJUrV7J48WKeeOKJ426Tk5NDTk6O73F6ejoAkZGRCk4iIiIiIlKuS3hq5eAQLVu2JDg4mL59+3LHHXdw0003HXfbSZMmERUV5VvUTU9ERERERCqqVganhQsXsmzZMt544w1efvllPv744+NuO3HiRFJTU33Lzp07a7BSERERERGpC2plV72EhAQATjrpJPbu3cujjz7K1VdfXeK2wcHBBAcH12R5IiIiIiJSx9TK4FSUZVnFrmESERERkfrD7XaTl5dndxnixwIDA3G5XCe8H1uDU0ZGBps3b/Y9TkxMZNWqVURHR9OqVSsmTpzI7t27ee+99wB47bXXaNWqFZ07dwbMvE7PP/88d911ly31i4iIiIh9MjIy2LVrF34ySLT4KYfDQcuWLWnQoMEJ7cfW4LRs2TLOPPNM3+MJEyYAMGbMGKZOnUpSUhI7duzwPe/xeJg4cSKJiYkEBATQrl07nn76aW699dYar11ERERE7ON2u9m1axdhYWE0adKkXKOiSf1jWRb79u1j165ddOjQ4YRanvxmHqeakpaWRlRUFKmpqRqOXERERKSWys7OJjExkTZt2hAaGmp3OeLHjhw5wrZt20hISCAkJKTYcxXJBrVyVD0RERERESjf/DtSv1XVMaLgJCIiIiIiUgYFJxERERERkTIoOImIiIiI1GJDhgxh/Pjx5d5+27ZtOBwOVq1aVW011UUKTiIiIiIiNcDhcJS6XHfddZXa7+eff84///nPcm8fHx9PUlIS3bt3r9T7lVddC2i1fgJcEREREZHaICkpyXd/+vTpPPzww2zYsMG37ujRAfPy8ggMDCxzv9HR0RWqw+VyERcXV6HXiFqcbPXlqt0Mf3kBT36z1u5SRERERGo1y7LIys23ZSnv7D5xcXG+JSoqCofD4XucnZ1Nw4YN+eSTTxgyZAghISF88MEHHDhwgKuvvpqWLVsSFhbGSSedxMcff1xsv0d31WvTpg1PPfUUN9xwAxEREbRq1Yr//Oc/vuePbgn6+eefcTgc/PDDD/Tt25ewsDD69+9fLNQBPPHEEzRt2pSIiAhuuukmHnjgAXr27Fmp3xdATk4O48aNo2nTpoSEhHDGGWfw22+/+Z4/dOgQ11xzDU2aNCE0NJQOHTrwzjvvAJCbm8udd95Js2bNCAkJoU2bNkyaNKnStZSHWpxslJnjZn1yOvHRYXaXIiIiIlKrHclz0/Xh721577WPn0tYUNV8rb7//vt54YUXeOeddwgODiY7O5s+ffpw//33ExkZyTfffMOoUaNo27Ytp5566nH388ILL/DPf/6Tv//978yYMYPbbruNQYMG0blz5+O+5sEHH+SFF16gSZMmjB07lhtuuIFffvkFgA8//JAnn3yS119/nQEDBjBt2jReeOEFEhISKv1Z77vvPj777DPeffddWrduzbPPPsu5557L5s2biY6O5qGHHmLt2rV8++23xMTEsHnzZo4cOQLAK6+8wqxZs/jkk09o1aoVO3fuZOfOnZWupTwUnGwUGWp+/GlH8myuRERERET8wfjx47nkkkuKrfvb3/7mu3/XXXfx3Xff8emnn5YanM477zxuv/12wISxl156iZ9//rnU4PTkk08yePBgAB544AHOP/98srOzCQkJ4dVXX+XGG2/k+uuvB+Dhhx9mzpw5ZGRkVOpzZmZmMmXKFKZOncqIESMAePPNN5k7dy5vvfUW9957Lzt27KBXr1707dsXMC1pXjt27KBDhw6cccYZOBwOWrduXak6KkLByUaRIabPalp2vs2ViIiIiNRuoYEu1j5+rm3vXVW8IcHL7Xbz9NNPM336dHbv3k1OTg45OTmEh4eXup+TTz7Zd9/bJTAlJaXcr2nWrBkAKSkptGrVig0bNviCmFe/fv348ccfy/W5jrZlyxby8vIYMGCAb11gYCD9+vVj3bp1ANx2221ceumlrFixgmHDhjFy5Ej69+8PwHXXXcc555xDp06dGD58OBdccAHDhg2rVC3lpeBko8jQguCkFicRERGRE+JwOKqsu5ydjg5EL7zwAi+99BIvv/wyJ510EuHh4YwfP57c3NxS93P0oBIOhwOPx1Pu1zgcDoBir/Gu8yrvtV0l8b62pH16140YMYLt27fzzTffMG/ePM4++2zuuOMOnn/+eXr37k1iYiLffvst8+bN44orrmDo0KHMmDGj0jWVRYND2CgypKCrXraCk4iIiIgca+HChVx88cVce+219OjRg7Zt27Jp06Yar6NTp04sXbq02Lply5ZVen/t27cnKCiIRYsW+dbl5eWxbNkyunTp4lvXpEkTrrvuOj744ANefvnlYoNcREZGcuWVV/Lmm28yffp0PvvsMw4ePFjpmspS+2N5LRZR0FUvIycfj8fC6XSU8QoRERERqU/at2/PZ599xuLFi2nUqBEvvvgiycnJxcJFTbjrrru4+eab6du3L/3792f69On8/vvvtG3btszXHj06H0DXrl257bbbuPfee4mOjqZVq1Y8++yzZGVlceONNwLmOqo+ffrQrVs3cnJy+Prrr32f+6WXXqJZs2b07NkTp9PJp59+SlxcHA0bNqzSz12UgpONIgpanCwL0nPyiQote5x+EREREak/HnroIRITEzn33HMJCwvjlltuYeTIkaSmptZoHddccw1bt27lb3/7G9nZ2VxxxRVcd911x7RCleSqq646Zl1iYiJPP/00Ho+HUaNGkZ6eTt++ffn+++9p1KgRAEFBQUycOJFt27YRGhrKwIEDmTZtGgANGjTgmWeeYdOmTbhcLk455RRmz56N01l9Heoc1ol0TqyF0tLSiIqKIjU1lcjISLvLodM/viUn38Oi+8+kZSMNSy4iIiJSHtnZ2SQmJpKQkEBISIjd5dRL55xzDnFxcbz//vt2l1Kq0o6VimQDtTjZLDI0kH3pOaQdyYdGdlcjIiIiInKsrKws3njjDc4991xcLhcff/wx8+bNY+7cuXaXVmMUnGwWGRJggpMGiBARERERP+VwOJg9ezZPPPEEOTk5dOrUic8++4yhQ4faXVqNUXCymYYkFxERERF/Fxoayrx58+wuw1YajtxmEZoEV0RERETE7yk42cw3l5NanERERERE/JaCk828XfXS1eIkIiIiIuK3FJxsFunrqqcWJxERERERf6XgZLPIUHXVExERERHxdwpONlOLk4iIiIiI/1NwslmEb3AIXeMkIiIiIlVn6tSpNGzY0O4y6gwFJ5v55nFSi5OIiIhIneZwOEpdrrvuukrvu02bNrz88svF1l155ZVs3LjxxIouh/oS0DQBrs28XfU0qp6IiIhI3ZaUlOS7P336dB5++GE2bNjgWxcaGlql7xcaGlrl+6zP1OJksyjv4BBqcRIRERGpPMuC3Ex7FssqV4lxcXG+JSoqCofDUWzdggUL6NOnDyEhIbRt25bHHnuM/PzCk+uPPvoorVq1Ijg4mObNmzNu3DgAhgwZwvbt27nnnnt8rVdwbEvQo48+Ss+ePXn//fdp06YNUVFRXHXVVaSnp/u2SU9P55prriE8PJxmzZrx0ksvMWTIEMaPH1/pX82OHTu4+OKLadCgAZGRkVxxxRXs3bvX9/zq1as588wziYiIIDIykj59+rBs2TIAtm/fzoUXXkijRo0IDw+nW7duzJ49u9K1nAi1ONnMNzjEkTwsy/Id6CIiIiJSAXlZ8FRze97773sgKPyEdvH9999z7bXX8sorrzBw4EC2bNnCLbfcAsAjjzzCjBkzeOmll5g2bRrdunUjOTmZ1atXA/D555/To0cPbrnlFm6++eZS32fLli3MnDmTr7/+mkOHDnHFFVfw9NNP8+STTwIwYcIEfvnlF2bNmkVsbCwPP/wwK1asoGfPnpX6XJZlMXLkSMLDw5k/fz75+fncfvvtXHnllfz8888AXHPNNfTq1YspU6bgcrlYtWoVgYHmO/Idd9xBbm4uCxYsIDw8nLVr19KgQYNK1XKiFJxs5r3GyWNBZq6bBsH6lYiIiIjUN08++SQPPPAAY8aMAaBt27b885//5L777uORRx5hx44dxMXFMXToUAIDA2nVqhX9+vUDIDo6GpfLRUREBHFxcaW+j8fjYerUqURERAAwatQofvjhB5588knS09N59913+eijjzj77LMBeOedd2jevPKBdN68efz+++8kJiYSHx8PwPvvv0+3bt347bffOOWUU9ixYwf33nsvnTt3BqBDhw6+1+/YsYNLL72Uk046yfdzsYu+pdssOMBJoMtBntsi7UiegpOIiIhIZQSGmZYfu977BC1fvpzffvvN1/ID4Ha7yc7OJisri8svv5yXX36Ztm3bMnz4cM477zwuvPBCAgIq9t2xTZs2vtAE0KxZM1JSUgDYunUreXl5vkAGEBUVRadOnSr9udatW0d8fLwvNAF07dqVhg0bsm7dOk455RQmTJjATTfdxPvvv8/QoUO5/PLLadeuHQDjxo3jtttuY86cOQwdOpRLL72Uk08+udL1nAhd42Qzh8OhuZxERERETpTDYbrL2bFUwaUWHo+Hxx57jFWrVvmWNWvWsGnTJkJCQoiPj2fDhg289tprhIaGcvvttzNo0CDy8ir2/dHbBa7wx+bA4/EApludd11RVjmv4SrJ8S5FKbr+0Ucf5c8//+T888/nxx9/pGvXrnzxxRcA3HTTTWzdupVRo0axZs0a+vbty6uvvlrpek6EgpMf8HbX08h6IiIiIvVT79692bBhA+3btz9mcTrNV/bQ0FAuuugiXnnlFX7++Wd+/fVX1qxZA0BQUBBut/uEamjXrh2BgYEsXbrUty4tLY1NmzZVep9du3Zlx44d7Ny507du7dq1pKam0qVLF9+6jh07cs899zBnzhwuueQS3nnnHd9z8fHxjB07ls8//5y//vWvvPnmm5Wu50SoX5gfiPRNgqsWJxEREZH66OGHH+aCCy4gPj6eyy+/HKfTye+//86aNWt44oknmDp1Km63m1NPPZWwsDDef/99QkNDad26NWC64C1YsICrrrqK4OBgYmJiKlxDREQEY8aM4d577yU6OpqmTZvyyCOP4HQ6yxzAzO12s2rVqmLrgoKCGDp0KCeffDLXXHMNL7/8sm9wiMGDB9O3b1+OHDnCvffey2WXXUZCQgK7du3it99+49JLLwVg/PjxjBgxgo4dO3Lo0CF+/PHHYoGrJik4+QFNgisiIiJSv5177rl8/fXXPP74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6bYKWxW8wdp7gsLbwuFymp93QJFrOV1FfmDFusoBIYGuKu2iWBFHByrvyY08bw+Bgt4BeQW/C6fDUaybZ6CzsBurx7KK9ESwivxsTctLUIDp4uhrUQwobFUsdnLrqBNXRU+IeIpsZ0FhV+qC362vCzQUaVXy+LoF5hWcDCr6Ow4q6Jbr7Z6Y77GK/W7N53CT452rsWhX4yIncgKdhdf/elupgl0uAgPMtr6TRQX/Tr33PZZFw9Ag26ceqUg20FV2IiIicgyHw+Hr5kMNjrnhOCqglMTFiX3ZdjodBDtdBAcAVTBwmwsHgS5zr6Kqqoby8v5Owyr4Ow0AggMq/vn8VdHW26JX04VW4ncIJgRWhl3BUSpHE5+IiIiIiIiUQcFJRERERESkDApOIiIiIiIiZVBwEhERERERKYOCk4iIiIiISBkUnERERERERMqg4CQiIiIiIlIGBScREREREZEyKDiJiIiIiIiUQcFJRERERESkDApOIiIiIiIiZVBwEhERERERKYOCk4iIiIiISBkUnERERERERMoQYHcBNc2yLADS0tJsrkREREREROzkzQTejFCaehec0tPTAYiPj7e5EhERERER8Qfp6elERUWVuo3DKk+8qkM8Hg979uwhIiICh8NRI++ZlpZGfHw8O3fuJDIyskbeU2o/HTdSGTpupLJ07Ehl6LiRyvCn48ayLNLT02nevDlOZ+lXMdW7Fien00nLli1tee/IyEjbDw6pfXTcSGXouJHK0rEjlaHjRirDX46bslqavDQ4hIiIiIiISBkUnERERERERMqg4FQDgoODeeSRRwgODra7FKlFdNxIZei4kcrSsSOVoeNGKqO2Hjf1bnAIERERERGRilKLk4iIiIiISBkUnERERERERMqg4CQiIiIiIlIGBScREREREZEyKDhVs9dff52EhARCQkLo06cPCxcutLsk8SOTJk3ilFNOISIigqZNmzJy5Eg2bNhQbBvLsnj00Udp3rw5oaGhDBkyhD///NOmisUfTZo0CYfDwfjx433rdNzI8ezevZtrr72Wxo0bExYWRs+ePVm+fLnveR07crT8/Hz+8Y9/kJCQQGhoKG3btuXxxx/H4/H4ttFxIwsWLODCCy+kefPmOBwOZs6cWez58hwjOTk53HXXXcTExBAeHs5FF13Erl27avBTlE7BqRpNnz6d8ePH8+CDD7Jy5UoGDhzIiBEj2LFjh92liZ+YP38+d9xxB0uWLGHu3Lnk5+czbNgwMjMzfds8++yzvPjii0yePJnffvuNuLg4zjnnHNLT022sXPzFb7/9xn/+8x9OPvnkYut13EhJDh06xIABAwgMDOTbb79l7dq1vPDCCzRs2NC3jY4dOdozzzzDG2+8weTJk1m3bh3PPvsszz33HK+++qpvGx03kpmZSY8ePZg8eXKJz5fnGBk/fjxffPEF06ZNY9GiRWRkZHDBBRfgdrtr6mOUzpJq069fP2vs2LHF1nXu3Nl64IEHbKpI/F1KSooFWPPnz7csy7I8Ho8VFxdnPf30075tsrOzraioKOuNN96wq0zxE+np6VaHDh2suXPnWoMHD7buvvtuy7J03Mjx3X///dYZZ5xx3Od17EhJzj//fOuGG24otu6SSy6xrr32WsuydNzIsQDriy++8D0uzzFy+PBhKzAw0Jo2bZpvm927d1tOp9P67rvvaqz20qjFqZrk5uayfPlyhg0bVmz9sGHDWLx4sU1Vib9LTU0FIDo6GoDExESSk5OLHUfBwcEMHjxYx5Fwxx13cP755zN06NBi63XcyPHMmjWLvn37cvnll9O0aVN69erFm2++6Xtex46U5IwzzuCHH35g48aNAKxevZpFixZx3nnnATpupGzlOUaWL19OXl5esW2aN29O9+7d/eY4CrC7gLpq//79uN1uYmNji62PjY0lOTnZpqrEn1mWxYQJEzjjjDPo3r07gO9YKek42r59e43XKP5j2rRprFixgt9+++2Y53TcyPFs3bqVKVOmMGHCBP7+97+zdOlSxo0bR3BwMKNHj9axIyW6//77SU1NpXPnzrhcLtxuN08++SRXX301oL85UrbyHCPJyckEBQXRqFGjY7bxl+/OCk7VzOFwFHtsWdYx60QA7rzzTn7//XcWLVp0zHM6jqSonTt3cvfddzNnzhxCQkKOu52OGzmax+Ohb9++PPXUUwD06tWLP//8kylTpjB69Gjfdjp2pKjp06fzwQcf8NFHH9GtWzdWrVrF+PHjad68OWPGjPFtp+NGylKZY8SfjiN11asmMTExuFyuYxJySkrKMWlb5K677mLWrFn89NNPtGzZ0rc+Li4OQMeRFLN8+XJSUlLo06cPAQEBBAQEMH/+fF555RUCAgJ8x4aOGzlas2bN6Nq1a7F1Xbp08Q1apL85UpJ7772XBx54gKuuuoqTTjqJUaNGcc899zBp0iRAx42UrTzHSFxcHLm5uRw6dOi429hNwamaBAUF0adPH+bOnVts/dy5c+nfv79NVYm/sSyLO++8k88//5wff/yRhISEYs8nJCQQFxdX7DjKzc1l/vz5Oo7qsbPPPps1a9awatUq39K3b1+uueYaVq1aRdu2bXXcSIkGDBhwzJQHGzdupHXr1oD+5kjJsrKycDqLf2V0uVy+4ch13EhZynOM9OnTh8DAwGLbJCUl8ccff/jPcWTbsBT1wLRp06zAwEDrrbfestauXWuNHz/eCg8Pt7Zt22Z3aeInbrvtNisqKsr6+eefraSkJN+SlZXl2+bpp5+2oqKirM8//9xas2aNdfXVV1vNmjWz0tLSbKxc/E3RUfUsS8eNlGzp0qVWQECA9eSTT1qbNm2yPvzwQyssLMz64IMPfNvo2JGjjRkzxmrRooX19ddfW4mJidbnn39uxcTEWPfdd59vGx03kp6ebq1cudJauXKlBVgvvviitXLlSmv79u2WZZXvGBk7dqzVsmVLa968edaKFSuss846y+rRo4eVn59v18cqRsGpmr322mtW69atraCgIKt3796+YaZFLMsM11nS8s477/i28Xg81iOPPGLFxcVZwcHB1qBBg6w1a9bYV7T4paODk44bOZ6vvvrK6t69uxUcHGx17tzZ+s9//lPseR07crS0tDTr7rvvtlq1amWFhIRYbdu2tR588EErJyfHt42OG/npp59K/E4zZswYy7LKd4wcOXLEuvPOO63o6GgrNDTUuuCCC6wdO3bY8GlK5rAsy7KnrUtERERERKR20DVOIiIiIiIiZVBwEhERERERKYOCk4iIiIiISBkUnERERERERMqg4CQiIiIiIlIGBScREREREZEyKDiJiIiIiIiUQcFJRESkAhwOBzNnzrS7DBERqWEKTiIiUmtcd911OByOY5bhw4fbXZqIiNRxAXYXICIiUhHDhw/nnXfeKbYuODjYpmpERKS+UIuTiIjUKsHBwcTFxRVbGjVqBJhudFOmTGHEiBGEhoaSkJDAp59+Wuz1a9as4ayzziI0NJTGjRtzyy23kJGRUWybt99+m27duhEcHEyzZs248847iz2/f/9+/u///o+wsDA6dOjArFmzqvdDi4iI7RScRESkTnnooYe49NJLWb16Nddeey1XX30169atAyArK4vhw4fTqFEjfvvtNz799FPmzZtXLBhNmTKFO+64g1tuuYU1a9Ywa9Ys2rdvX+w9HnvsMa644gp+//13zjvvPK655hoOHjxYo59TRERqlsOyLMvuIkRERMrjuuuu44MPPiAkJKTY+vvvv5+HHnoIh8PB2LFjmTJliu+50047jd69e/P666/z5ptvcv/997Nz507Cw8MBmD17NhdeeCF79uwhNjaWFi1acP311/PEE0+UWIPD4eAf//gH//znPwHIzMwkIiKC2bNn61orEZE6TNc4iYhIrXLmmWcWC0YA0dHRvvunn356sedOP/10Vq1aBcC6devo0aOHLzQBDBgwAI/Hw4YNG3A4HOzZs4ezzz671BpOPvlk3/3w8HAiIiJISUmp7EcSEZFaQMFJRERqlfDw8GO6zpXF4XAAYFmW735J24SGhpZrf4GBgce81uPxVKgmERGpXXSNk4iI1ClLliw55nHnzp0B6Nq1K6tWrSIzM9P3/C+//ILT6aRjx45ERETQpk0bfvjhhxqtWURE/J9anEREpFbJyckhOTm52LqAgABiYmIA+PTTT+nbty9nnHEGH374IUuXLuWtt94C4JprruGRRx5hzJgxPProo+zbt4+77rqLUaNGERsbC8Cjjz7K2LFjadq0KSNGjCA9PZ1ffvmFu+66q2Y/qIiI+BUFJxERqVW+++47mjVrVmxdp06dWL9+PWBGvJs2bRq33347cXFxfPjhh3Tt2hWAsLAwvv/+e+6++25OOeUUwsLCuPTSS3nxxRd9+xozZgzZ2dm89NJL/O1vfyMmJobLLrus5j6giIj4JY2qJyIidYbD4eCLL75g5MiRdpciIiJ1jK5xEhERERERKYOCk4iIiIiISBl0jZOIiNQZ6n0uIiLVRS1OIiIiIiIiZVBwEhERERERKYOCk4iIiIiISBkUnERERERERMqg4CQiIiIiIlIGBScREREREZEyKDiJiIiIiIiUQcFJRERERESkDApOIiIiIiIiZfh/jlEsU5RzvgUAAAAASUVORK5CYII=",
      "text/plain": [
       "<Figure size 1000x500 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "\n",
    "# 计算和打印训练集和交叉验证集的准确率\n",
    "FNNmodel.eval()\n",
    "with torch.no_grad():\n",
    "   # 训练集准确率\n",
    "    TrainCorrect = 0\n",
    "    TrainTotal = 0\n",
    "    for images, labels in TrainLoader:\n",
    "        outputs = FNNmodel(images)\n",
    "        _, predicted = torch.max(outputs, 1)\n",
    "        _, true_labels = torch.max(labels, 1)\n",
    "        TrainTotal += labels.size(0)\n",
    "        TrainCorrect += (predicted == true_labels).sum().item()\n",
    "    \n",
    "    TrainAccuracy = 100 * TrainCorrect / TrainTotal\n",
    "    \n",
    "   # 测试集精度\n",
    "    TestCorrect = 0\n",
    "    TestTotal = 0\n",
    "    for images, labels in TestLoader:\n",
    "        outputs = FNNmodel(images)\n",
    "        _, predicted = torch.max(outputs, 1)\n",
    "        _, true_labels = torch.max(labels, 1)\n",
    "        TestTotal += labels.size(0)\n",
    "        TestCorrect += (predicted == true_labels).sum().item()\n",
    "    \n",
    "    TestAccuracy = 100 * TestCorrect / TestTotal\n",
    "\n",
    "print(f'Accuracy on training set: {TrainAccuracy:.2f}%')\n",
    "print(f'Accuracy on cross-validation set: {TestAccuracy:.2f}%')\n",
    "\n",
    "# 绘制训练和交叉验证损失\n",
    "plt.figure(figsize=(10, 5))\n",
    "plt.plot(range(1, num_epochs+1), TrainLosses, label='Training Loss')\n",
    "plt.plot(range(1, num_epochs+1), TestLosses, label='Testing Loss')\n",
    "plt.xlabel('Epoch')\n",
    "plt.ylabel('Loss')\n",
    "plt.legend()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 170,
   "id": "c19475ff-047a-41c8-bad5-5adca95943fe",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "测试集大小: 10000\n",
      "训练集大小: 60000\n",
      "训练样本数量: 43360\n",
      "交叉验证样本数量: 10850\n"
     ]
    }
   ],
   "source": [
    "import torch\n",
    "import torchvision\n",
    "import torchvision.transforms as transforms\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "label_size = 18  # 标签字体大小\n",
    "ticklabel_size = 14  # 刻度标签字体大小\n",
    "\n",
    "class FlattenTransform:\n",
    "    def __call__(self, tensor):\n",
    "        ''' \n",
    "        Flatten tensor into an 1-D vector\n",
    "        '''\n",
    "        return tensor.view(-1)\n",
    "\n",
    "# 定义一个数据归一化的转换操作\n",
    "transform = transforms.Compose([\n",
    "    transforms.ToTensor(),  # 将数据转换为张量\n",
    "    FlattenTransform()  # 展平张量\n",
    "])\n",
    "\n",
    "# 从MNIST加载测试数据\n",
    "TestSize = torchvision.datasets.MNIST(root='D:/BaiduNetdiskDownload', train=False, download=False, transform=transform)\n",
    "print(f\"测试集大小: {len(TestSize)}\")\n",
    "\n",
    "# 从MNIST加载训练数据\n",
    "TrainSize = torchvision.datasets.MNIST(root='D:/BaiduNetdiskDownload', train=True, download=False, transform=transform)\n",
    "print(f\"训练集大小: {len(TrainSize)}\")\n",
    "\n",
    "# trX（训练数据）和cvX（验证数据）的比例\n",
    "TrCvRate = 0.8\n",
    "\n",
    "# 创建一个列表来存储每个类别的索引（MNIST中有10个类别）\n",
    "CIndices = [[] for _ in range(10)]\n",
    "\n",
    "# CIndices\n",
    "for idx, (_, label) in enumerate(TrainSize):\n",
    "    CIndices[label].append(idx)\n",
    "\n",
    "# 计算训练集和验证集中每个类别的样本数量\n",
    "TrainSize_perclass = int(TrCvRate * min(len(indices) for indices in CIndices))\n",
    "valSize_perclass = min(len(indices) for indices in CIndices) - train_size_per_class\n",
    "\n",
    "# 创建平衡的训练集和验证集\n",
    "TrainIndices = []\n",
    "ValIndices = []\n",
    "for indices in CIndices:\n",
    "    TrainIndices.extend(indices[:TrainSize_perclass])\n",
    "    ValIndices.extend(indices[TrainSize_perclass:TrainSize_perclass + valSize_perclass])\n",
    "\n",
    "# 创建子集数据集\n",
    "from torch.utils.data import Subset\n",
    "TrainX = Subset(TrainSize, TrainIndices)\n",
    "CVX = Subset(TrainSize, ValIndices)\n",
    "\n",
    "print(f\"训练样本数量: {len(TrainX)}\")\n",
    "print(f\"交叉验证样本数量: {len(CVX)}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 176,
   "id": "83872fd0-22f8-4ec9-9905-3922fba9741f",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Input_size is 784\n",
      "tensor([[0., 1., 0., 0., 0., 0., 0., 0., 0., 0.],\n",
      "        [0., 0., 0., 1., 0., 0., 0., 0., 0., 0.],\n",
      "        [0., 0., 0., 0., 0., 0., 0., 1., 0., 0.],\n",
      "        [0., 0., 0., 0., 0., 1., 0., 0., 0., 0.],\n",
      "        [0., 0., 0., 0., 0., 0., 0., 0., 1., 0.],\n",
      "        [0., 0., 0., 0., 0., 0., 0., 1., 0., 0.],\n",
      "        [0., 0., 1., 0., 0., 0., 0., 0., 0., 0.],\n",
      "        [0., 0., 0., 1., 0., 0., 0., 0., 0., 0.],\n",
      "        [0., 0., 0., 0., 0., 0., 0., 0., 1., 0.],\n",
      "        [0., 0., 0., 0., 0., 0., 0., 0., 0., 1.],\n",
      "        [0., 0., 0., 0., 0., 0., 1., 0., 0., 0.],\n",
      "        [0., 0., 0., 0., 0., 0., 0., 0., 1., 0.],\n",
      "        [0., 1., 0., 0., 0., 0., 0., 0., 0., 0.],\n",
      "        [0., 0., 0., 0., 0., 0., 0., 1., 0., 0.],\n",
      "        [0., 0., 0., 0., 1., 0., 0., 0., 0., 0.],\n",
      "        [0., 0., 0., 0., 1., 0., 0., 0., 0., 0.],\n",
      "        [0., 0., 0., 0., 0., 0., 1., 0., 0., 0.],\n",
      "        [0., 0., 0., 0., 0., 0., 1., 0., 0., 0.],\n",
      "        [1., 0., 0., 0., 0., 0., 0., 0., 0., 0.],\n",
      "        [0., 0., 0., 0., 0., 1., 0., 0., 0., 0.],\n",
      "        [0., 0., 0., 0., 0., 0., 0., 0., 1., 0.],\n",
      "        [0., 1., 0., 0., 0., 0., 0., 0., 0., 0.],\n",
      "        [0., 1., 0., 0., 0., 0., 0., 0., 0., 0.],\n",
      "        [0., 0., 0., 0., 0., 0., 0., 0., 0., 1.],\n",
      "        [0., 0., 0., 0., 0., 0., 0., 0., 0., 1.],\n",
      "        [0., 0., 0., 0., 0., 1., 0., 0., 0., 0.],\n",
      "        [0., 0., 0., 1., 0., 0., 0., 0., 0., 0.],\n",
      "        [0., 0., 0., 1., 0., 0., 0., 0., 0., 0.],\n",
      "        [0., 0., 1., 0., 0., 0., 0., 0., 0., 0.],\n",
      "        [0., 0., 0., 1., 0., 0., 0., 0., 0., 0.],\n",
      "        [0., 0., 0., 0., 0., 1., 0., 0., 0., 0.],\n",
      "        [0., 0., 0., 0., 1., 0., 0., 0., 0., 0.],\n",
      "        [0., 0., 0., 0., 0., 0., 0., 0., 0., 1.],\n",
      "        [0., 1., 0., 0., 0., 0., 0., 0., 0., 0.],\n",
      "        [0., 0., 0., 0., 0., 1., 0., 0., 0., 0.],\n",
      "        [0., 0., 0., 0., 0., 0., 0., 0., 1., 0.],\n",
      "        [0., 0., 0., 0., 0., 0., 0., 0., 0., 1.],\n",
      "        [0., 0., 0., 0., 0., 0., 0., 1., 0., 0.],\n",
      "        [0., 0., 0., 0., 0., 0., 0., 1., 0., 0.],\n",
      "        [1., 0., 0., 0., 0., 0., 0., 0., 0., 0.],\n",
      "        [0., 0., 1., 0., 0., 0., 0., 0., 0., 0.],\n",
      "        [0., 0., 0., 0., 0., 1., 0., 0., 0., 0.]])\n"
     ]
    }
   ],
   "source": [
    "batch_size = 42 # 定义训练批次\n",
    "\n",
    "def one_hot_collate(batch):\n",
    "    data = torch.stack([item[0] for item in batch])\n",
    "    labels = torch.tensor([item[1] for item in batch])\n",
    "    one_hot_labels = torch.zeros(labels.size(0), 10)  # 10 classes in MNIST\n",
    "    one_hot_labels.scatter_(1, labels.unsqueeze(1), 1)\n",
    "    return data, one_hot_labels\n",
    "\n",
    "TrainLoader = torch.utils.data.DataLoader(TrainX, batch_size=batch_size, shuffle=True, num_workers=0, collate_fn=one_hot_collate)\n",
    "CVLoader = torch.utils.data.DataLoader(CVX, batch_size=batch_size, shuffle=False, num_workers=0, collate_fn=one_hot_collate)\n",
    "TestLoader = torch.utils.data.DataLoader(TestSize, batch_size=batch_size, shuffle=False, num_workers=0, collate_fn=one_hot_collate)\n",
    "\n",
    "# 获取一批训练数据\n",
    "dataX = iter(TrainLoader)\n",
    "data, labels = next(dataX)\n",
    "\n",
    "input_size = data[0].numpy().shape[0]\n",
    "print(f'Input_size is {input_size}')\n",
    "print(labels)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 178,
   "id": "7e497f4b-94fe-46b9-b6f8-e150e7fe7ce3",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "FNN(\n",
      "  (fc1): Linear(in_features=784, out_features=15, bias=True)\n",
      "  (relu1): ReLU()\n",
      "  (fc2): Linear(in_features=15, out_features=15, bias=True)\n",
      "  (relu2): ReLU()\n",
      "  (fc3): Linear(in_features=15, out_features=10, bias=True)\n",
      "  (softmax): Softmax(dim=1)\n",
      ")\n"
     ]
    }
   ],
   "source": [
    "import torch.nn as nn\n",
    "\n",
    "class FNN(nn.Module):\n",
    "    def __init__(self, input_size, hidden_size, num_classes):\n",
    "        super(FNN, self).__init__()\n",
    "        self.fc1 = nn.Linear(input_size, hidden_size)\n",
    "        self.relu1 = nn.ReLU()\n",
    "        self.fc2 = nn.Linear(hidden_size, hidden_size)\n",
    "        self.relu2 = nn.ReLU()\n",
    "        self.fc3 = nn.Linear(hidden_size, num_classes)\n",
    "        self.softmax = nn.Softmax(dim=1)\n",
    "    \n",
    "    def forward(self, x):\n",
    "        x = self.fc1(x)\n",
    "        x = self.relu1(x)\n",
    "        x = self.fc2(x)\n",
    "        x = self.relu2(x)\n",
    "        x = self.fc3(x)\n",
    "        out = self.softmax(x)\n",
    "        return out\n",
    "\n",
    "# 定义模型参数\n",
    "hidden_size = 15\n",
    "num_classes = 10 # MNIST 有 10 个类（数字 0-9）\n",
    "\n",
    "# 实例化模型\n",
    "FNNmodel = FNN(input_size, hidden_size, num_classes)\n",
    "print(FNNmodel)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 188,
   "id": "a41fde7d-3ebf-42de-ba95-f7b28209b89d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch [1/30], Train Loss: 1.8247, CV Loss: 1.7235\n",
      "Epoch [2/30], Train Loss: 1.7151, CV Loss: 1.7154\n",
      "Epoch [3/30], Train Loss: 1.7067, CV Loss: 1.7097\n",
      "Epoch [4/30], Train Loss: 1.7023, CV Loss: 1.7063\n",
      "Epoch [5/30], Train Loss: 1.6982, CV Loss: 1.7046\n",
      "Epoch [6/30], Train Loss: 1.6953, CV Loss: 1.7025\n",
      "Epoch [7/30], Train Loss: 1.6933, CV Loss: 1.7018\n",
      "Epoch [8/30], Train Loss: 1.6907, CV Loss: 1.6996\n",
      "Epoch [9/30], Train Loss: 1.6895, CV Loss: 1.6977\n",
      "Epoch [10/30], Train Loss: 1.6876, CV Loss: 1.6971\n",
      "Epoch [11/30], Train Loss: 1.6814, CV Loss: 1.6474\n",
      "Epoch [12/30], Train Loss: 1.6172, CV Loss: 1.6220\n",
      "Epoch [13/30], Train Loss: 1.5827, CV Loss: 1.5545\n",
      "Epoch [14/30], Train Loss: 1.5317, CV Loss: 1.5406\n",
      "Epoch [15/30], Train Loss: 1.5247, CV Loss: 1.5376\n",
      "Epoch [16/30], Train Loss: 1.5204, CV Loss: 1.5337\n",
      "Epoch [17/30], Train Loss: 1.5182, CV Loss: 1.5332\n",
      "Epoch [18/30], Train Loss: 1.5149, CV Loss: 1.5354\n",
      "Epoch [19/30], Train Loss: 1.5141, CV Loss: 1.5296\n",
      "Epoch [20/30], Train Loss: 1.5119, CV Loss: 1.5299\n",
      "Epoch [21/30], Train Loss: 1.5101, CV Loss: 1.5299\n",
      "Epoch [22/30], Train Loss: 1.5092, CV Loss: 1.5284\n",
      "Epoch [23/30], Train Loss: 1.5081, CV Loss: 1.5288\n",
      "Epoch [24/30], Train Loss: 1.5067, CV Loss: 1.5288\n",
      "Epoch [25/30], Train Loss: 1.5061, CV Loss: 1.5262\n",
      "Epoch [26/30], Train Loss: 1.5047, CV Loss: 1.5261\n",
      "Epoch [27/30], Train Loss: 1.5045, CV Loss: 1.5261\n",
      "Epoch [28/30], Train Loss: 1.5032, CV Loss: 1.5255\n",
      "Epoch [29/30], Train Loss: 1.5026, CV Loss: 1.5270\n",
      "Epoch [30/30], Train Loss: 1.5017, CV Loss: 1.5272\n"
     ]
    }
   ],
   "source": [
    "# 定义 loss 函数和优化器\n",
    "criterion = nn.CrossEntropyLoss()\n",
    "optimizer = torch.optim.Adam(FNNmodel.parameters())\n",
    "\n",
    "# 存储损失的列表\n",
    "TrainLosses = []\n",
    "CVLosses = []\n",
    "\n",
    "# epoch 数\n",
    "num_epochs = 30\n",
    "\n",
    "for epoch in range(num_epochs):\n",
    "    FNNmodel.train()\n",
    "    BatchLosses = []\n",
    "    \n",
    "    for BatchX, BatchY in TrainLoader:\n",
    "        # 正向传递\n",
    "        Batchoutputs = FNNmodel(BatchX)\n",
    "        Batchloss = criterion(Batchoutputs, BatchY)\n",
    "        \n",
    "        # 反向传递和优化\n",
    "        optimizer.zero_grad()\n",
    "        Batchloss.backward()\n",
    "        optimizer.step()\n",
    "        \n",
    "        BatchLosses.append(Batchloss.item())\n",
    "    \n",
    "   # 计算此 epoch 的平均训练损失\n",
    "    AvgTrainLoss = sum(BatchLosses) / len(BatchLosses)\n",
    "    TrainLosses.append(AvgTrainLoss)\n",
    "    \n",
    "    # 在交叉验证集上评估\n",
    "    FNNmodel.eval()\n",
    "    CVBatchLosses = []\n",
    "    with torch.no_grad():\n",
    "        for CVX, CVY in cvLoader:\n",
    "            CVOutputs = FNNmodel(CVX)\n",
    "            CVLoss = criterion(CVOutputs, CVY)\n",
    "            CVBatchLosses.append(CVLoss.item())\n",
    "    \n",
    "    AvgCVLoss = sum(CVBatchLosses) / len(CVBatchLosses)\n",
    "    CVLosses.append(AvgCVLoss)\n",
    "    \n",
    "    print(f'Epoch [{epoch+1}/{num_epochs}], Train Loss: {AvgTrainLoss:.4f}, CV Loss: {AvgCVLoss:.4f}')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 196,
   "id": "9011f7a1-c2df-40da-a32c-92777b78b0ed",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Accuracy on training set: 96.07%\n",
      "Accuracy on cross-validation set: 93.44%\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAA1cAAAHUCAYAAADWedKvAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguNCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8fJSN1AAAACXBIWXMAAA9hAAAPYQGoP6dpAACCfUlEQVR4nOzdd3xT9f7H8VeStumelA4oZW+oIENAlqAMRUAU3KCo163X67g48efALXpRvA7EiagIch1sERVQUAoIiOzRwe5u05Hz+yNtaGgLbWmbjvfz8TiPnJyVT5pG++b7Pd+vyTAMAxERERERETkrZncXICIiIiIiUh8oXImIiIiIiFQBhSsREREREZEqoHAlIiIiIiJSBRSuREREREREqoDClYiIiIiISBVQuBIREREREakCClciIiIiIiJVQOFKRERERESkCihciUiDYTKZyrWsXLnyrF5n6tSpmEymSp27cuXKKqmhtps0aRLNmzcv17F2u52PPvqIoUOH0qhRIzw9PWncuDGXXHIJ//vf/7Db7dVb7FnauHEjJpOJf//732Ues2PHDkwmE3fffXe5r1va79mgQYMYNGjQGc/du3cvJpOJ2bNnl/v1imzdupWpU6eyd+/eEvsq8rlWNZPJxJ133umW1xYRKeLh7gJERGrKmjVrXJ4/9dRT/PDDD6xYscJle8eOHc/qdW666SaGDx9eqXO7d+/OmjVrzrqG+iInJ4cxY8awZMkSrrzySmbOnElkZCRHjhxh0aJFXHHFFcydO5fRo0e7u9QyxcXFce655/Lhhx/yzDPPYLFYShzz/vvvAzB58uSzeq0333zzrM4vj61bt/Lkk08yaNCgEkHqscce45577qn2GkREaiuFKxFpMM477zyX5+Hh4ZjN5hLbT5WVlYWvr2+5X6dp06Y0bdq0UjUGBgaesZ6G5L777mPx4sV88MEHXH/99S77LrvsMh544AGys7PLPD8vLw+TyYSHh3v/dzd58mRuv/12vv/+ey655BKXfQUFBXz44Yece+65xMXFndXruDuUt2rVyq2vLyLibuoWKCJSzKBBg+jcuTOrVq2ib9+++Pr6cuONNwIwd+5cLrroIqKiovDx8aFDhw78+9//JjMz0+UapXXXat68OZdccgmLFi2ie/fu+Pj40L59e2bNmuVyXGndAidNmoS/vz87d+5k5MiR+Pv7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Pz8/58++yNl8R8pTi5+fH5dffrnL9qLaimrp2bMnAOPHj+fzzz8nISGhxLV69erFxo0buf3221m8eDFpaWlnXZ+I1A8KVyIip4iKiiqxLSMjg/79+/Prr7/y9NNPs3LlStatW8dXX30FcNpBFYqEhYWV2Ga1Wst1rq+vL97e3iXOzcnJcT4/duwYERERJc4tbVtpXnnlFW677TZ69+7NvHnzWLt2LevWrWP48OGl1njq+ynq3lV07LFjxwCIjIwscW5p205V9Ef2nj17ylV/kVM/v6I6Svtco6OjnftNJhPLly9n2LBhvPDCC3Tv3p3w8HDuvvtu0tPTAUf3t2XLltG4cWPuuOMOWrVqRatWrVzutRkyZAienp7OpSg4mUwmbrzxRjZv3sz69esBR5fAFi1aMHjwYKBqfs9Ofe+lff6l/fyvvvpqZsyYwU033cTixYv57bffWLduHeHh4RV+3eKv7+Hh4dINEhw/i8jISOfPvsjZfEfKU0tkZGSJ+yEbN26Mh4eHs5YBAwawYMEC8vPzuf7662natCmdO3dmzpw5znOmTJnCSy+9xNq1axkxYgRhYWEMGTLE+bmKSMOle65ERE5R2hxVK1asIDExkZUrVzpbEYBadY9FWFgYv/32W4ntpQ1eUJqPP/6YQYMGMXPmTJftRcGiMvWU9frlqWnw4MF4enqyYMECbr311nK/7qmfX1EdSUlJJY5NTEykUaNGzuexsbG89957APz99998/vnnTJ06ldzcXN566y0A+vfvT//+/SkoKGD9+vX85z//4d577yUiIoIrr7yS//73vy4/s+LXnzRpEo8//jizZs3C09OTDRs28NRTTzlrrurfs/L+TqSmpvLNN9/wxBNPuMzHZbPZnPfiVfb18/PzS9xnZhgGycnJzlaimhAWFsavv/6KYRguvyOHDx8mPz/f5XMaPXo0o0ePxmazsXbtWqZNm8bVV19N8+bN6dOnDx4eHtx3333cd999pKSksGzZMh5++GGGDRvGgQMHKjS6qIjUL2q5EhEph6I/xorffA/w3//+1x3llGrgwIGkp6fz/fffu2z/7LPPynW+yWQq8f42bdpUYn6w8mrXrh1RUVHMmTMHwzCc2/ft28fq1avPeH5kZKSzFaWsEQZ37drFpk2bTnudPn364OPjw8cff+yy/eDBg6xYsYIhQ4aUel7btm159NFH6dKlC3/88UeJ/RaLhd69e/PGG28AOI9p164dPXr0cC7FR8+Ljo5m+PDhzJkzhzfeeAOz2czEiROd+6v692zw4MGkp6ezcOFCl+2ffvqpy3OTyYRhGCVe991336WgoMBl26ktlKdT9LM99Wc/b948MjMzy/zZV4chQ4aQkZHBggULXLYX/W6VVovVamXgwIE8//zzAGzYsKHEMcHBwVx++eXccccdHD9+vNTJlUWk4VDLlYhIOfTt25eQkBBuvfVWnnjiCTw9Pfnkk0/YuHGju0tzmjhxIq+++irXXnstTz/9NK1bt+b7779n8eLFAGccne+SSy7hqaee4oknnmDgwIFs376d//u//6NFixbk5+dXuB6z2cxTTz3FTTfdxNixY7n55ptJSUlh6tSp5eoWCI6uirt372bSpEksXryYsWPHEhERwdGjR1m6dCnvv/8+n332mfN+r9IEBwfz2GOP8fDDD3P99ddz1VVXcezYMZ588km8vb154oknAEeQvPPOO7niiito06YNXl5erFixgk2bNjlbc9566y1WrFjBxRdfTLNmzcjJyXEOFT506NByvafJkyfz7bffOoeZj4mJce6r6t+z66+/nldffZXrr7+eZ555hjZt2vDdd985fyeKBAYGMmDAAF588UUaNWpE8+bN+fHHH3nvvfcIDg52ObZz584AvP322wQEBODt7U2LFi1K7dJ34YUXMmzYMB566CHS0tLo16+fc7TAbt26cd1111XqfZVl165dfPnllyW2d+zYkeuvv5433niDiRMnsnfvXrp06cLPP//Ms88+y8iRI52f3+OPP87BgwcZMmQITZs2JSUlhddee83l3rdRo0bRuXNnevToQXh4OPv27WP69OnExsY6R54UkQbKveNpiIi4T1mjBXbq1KnU41evXm306dPH8PX1NcLDw42bbrrJ+OOPP0qMulbWaIEXX3xxiWueOrpbWaMFnlpnWa+zf/9+47LLLjP8/f2NgIAAY9y4ccZ3331X6ohxp7LZbMb9999vNGnSxPD29ja6d+9uLFiwoMQIcEWjBb744oslrgEYTzzxhMu2d99912jTpo3h5eVltG3b1pg1a1aJa55Ofn6+8cEHHxgXXHCBERoaanh4eBjh4eHGiBEjjE8//dQoKCgwDOPkz+6LL74o9Trvvvuu0bVrV8PLy8sICgoyRo8ebWzZssW5/9ChQ8akSZOM9u3bG35+foa/v7/RtWtX49VXX3WOirhmzRpj7NixRmxsrGG1Wo2wsDBj4MCBxsKFC8v1XgzDMHJzc42IiIhSR64zjLP7PTv198kwDOPgwYPGuHHjXH4nVq9eXeJ6RceFhIQYAQEBxvDhw40///zTiI2NNSZOnOhyzenTpxstWrQwLBaLy3VK+1yzs7ONhx56yIiNjTU8PT2NqKgo47bbbjNOnDjhclx5vyNlAcpcin4njx07Ztx6661GVFSU4eHhYcTGxhpTpkwxcnJynNf55ptvjBEjRhhNmjQxvLy8jMaNGxsjR440fvrpJ+cxL7/8stG3b1+jUaNGhpeXl9GsWTNj8uTJxt69e89Yp4jUbybDKNZXQ0RE6p1nn32WRx99lP3799O0aVN3lyMiIlJvqVugiEg9MmPGDADat29PXl4eK1as4PXXX+faa69VsBIREalmClciIvWIr68vr776Knv37sVms9GsWTMeeughHn30UXeXJiIiUu+pW6CIiIiIiEgV0FDsIiIiIiIiVUDhSkREREREpAooXImIiIiIiFQBDWhRCrvdTmJiIgEBAZhMJneXIyIiIiIibmIYBunp6URHR2M2n75tSuGqFImJicTExLi7DBERERERqSUOHDhwxmlNFK5KERAQADh+gIGBgW6uRkRERERE3CUtLY2YmBhnRjgdhatSFHUFDAwMVLgSEREREZFy3S6kAS1ERERERESqgMKViIiIiIhIFVC4EhERERERqQK650pERESkgTMMg/z8fAoKCtxdiohbeHp6YrFYzvo6ClciIiIiDVhubi5JSUlkZWW5uxQRtzGZTDRt2hR/f/+zuo7ClYiIiEgDZbfb2bNnDxaLhejoaLy8vMo1IppIfWIYBkeOHOHgwYO0adPmrFqwFK5EREREGqjc3FzsdjsxMTH4+vq6uxwRtwkPD2fv3r3k5eWdVbjSgBYiIiIiDZzZrD8JpWGrqhZbfZNERERERESqgMKViIiIiIhIFVC4EhEREZEGb9CgQdx7773lPn7v3r2YTCbi4+OrrSapexSuRERERKTOMJlMp10mTZpUqet+9dVXPPXUU+U+PiYmhqSkJDp37lyp1ysvhbi6RaMF1hGGYWhoVBEREWnwkpKSnOtz587l8ccfZ/v27c5tPj4+Lsfn5eXh6el5xuuGhoZWqA6LxUJkZGSFzpH6Ty1Xtdw7q3Zzwcsr+XjtPneXIiIiIvWcYRhk5ea7ZTEMo1w1RkZGOpegoCBMJpPzeU5ODsHBwXz++ecMGjQIb29vPv74Y44dO8ZVV11F06ZN8fX1pUuXLsyZM8fluqd2C2zevDnPPvssN954IwEBATRr1oy3337buf/UFqWVK1diMplYvnw5PXr0wNfXl759+7oEP4Cnn36axo0bExAQwE033cS///1vzjnnnEp9XgA2m427776bxo0b4+3tzfnnn8+6deuc+0+cOME111xDeHg4Pj4+tGnThvfffx9wDMV/5513EhUVhbe3N82bN2fatGmVrkXUclXrpdvy2X0kk/gDqVzXx93ViIiISH2WnVdAx8cXu+W1t/7fMHy9quZP04ceeoiXX36Z999/H6vVSk5ODueeey4PPfQQgYGBfPvtt1x33XW0bNmS3r17l3mdl19+maeeeoqHH36YL7/8kttuu40BAwbQvn37Ms955JFHePnllwkPD+fWW2/lxhtv5JdffgHgk08+4ZlnnuHNN9+kX79+fPbZZ7z88su0aNGi0u/1wQcfZN68eXzwwQfExsbywgsvMGzYMHbu3EloaCiPPfYYW7du5fvvv6dRo0bs3LmT7OxsAF5//XUWLlzI559/TrNmzThw4AAHDhyodC2icFXrxTUNAmBzQop7CxERERGpI+69914uu+wyl23333+/c/2uu+5i0aJFfPHFF6cNVyNHjuT2228HHIHt1VdfZeXKlacNV8888wwDBw4E4N///jcXX3wxOTk5eHt785///IfJkydzww03APD444+zZMkSMjIyKvU+MzMzmTlzJrNnz2bEiBEAvPPOOyxdupT33nuPBx54gP3799OtWzd69OgBOFrkiuzfv582bdpw/vnnYzKZiI2NrVQdcpLCVS3XpYkjXO08nEGmLR8/qz4yERERqR4+nha2/t8wt712VSkKEkUKCgp47rnnmDt3LgkJCdhsNmw2G35+fqe9TteuXZ3rRd0PDx8+XO5zoqKiADh8+DDNmjVj+/btzrBWpFevXqxYsaJc7+tUu3btIi8vj379+jm3eXp60qtXL7Zt2wbAbbfdxrhx4/jjjz+46KKLGDNmDH379gVg0qRJXHjhhbRr147hw4dzySWXcNFFF1WqFnHQPVe1XONAbyIDvbEbsCUxzd3liIiISD1mMpnw9fJwy1KVA3edGppefvllXn31VR588EFWrFhBfHw8w4YNIzc397TXOXUgDJPJhN1uL/c5Re+p+Dmnvs/y3mtWmqJzS7tm0bYRI0awb98+7r33XhITExkyZIizFa979+7s2bOHp556iuzsbMaPH8/ll19e6XpE4apO6FrYNXDTwRT3FiIiIiJSB/3000+MHj2aa6+9lri4OFq2bMmOHTtqvI527drx22+/uWxbv359pa/XunVrvLy8+Pnnn53b8vLyWL9+PR06dHBuCw8PZ9KkSXz88cdMnz7dZWCOwMBAJkyYwDvvvMPcuXOZN28ex48fr3RNDZ36mNUBXZsGsWTrITYnpLq7FBEREZE6p3Xr1sybN4/Vq1cTEhLCK6+8QnJysksAqQl33XUXN998Mz169KBv377MnTuXTZs20bJlyzOee+qogwAdO3bktttu44EHHiA0NJRmzZrxwgsvkJWVxeTJkwHHfV3nnnsunTp1wmaz8c033zjf96uvvkpUVBTnnHMOZrOZL774gsjISIKDg6v0fTckCld1QJemwQBsOqhwJSIiIlJRjz32GHv27GHYsGH4+vpyyy23MGbMGFJTa/Zvq2uuuYbdu3dz//33k5OTw/jx45k0aVKJ1qzSXHnllSW27dmzh+eeew673c51111Heno6PXr0YPHixYSEhADg5eXFlClT2Lt3Lz4+PvTv35/PPvsMAH9/f55//nl27NiBxWKhZ8+efPfdd5jN6txWWSbjbDp61lNpaWkEBQWRmppKYGCgu8vhRGYu3Z5aCsDGJy4iyOfME+GJiIiInElOTg579uyhRYsWeHt7u7ucBunCCy8kMjKSjz76yN2lNGin+y5UJBuo5aoOCPHzIibUhwPHs9mSkErf1o3cXZKIiIiIVFBWVhZvvfUWw4YNw2KxMGfOHJYtW8bSpUvdXZpUEbX51RFdmwQDsFFdA0VERETqJJPJxHfffUf//v0599xz+d///se8efMYOnSou0uTKqKWqzqia9Mgvt2cpMmERUREROooHx8fli1b5u4ypBqp5aqO6OIcjl0tVyIiIiIitZHCVR3RuYkjXB08kc2xDJubqxERERERkVMpXNURgd6etAx3zDau+a5ERERERGofhas6pGth69VmdQ0UEREREal1FK7qkKLJhDVioIiIiIhI7aNwVYfEFQ5q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jq5du1aqlvJy6z1XmZmZxMXFMWPGjHId//PPP3P99dczefJktmzZwhdffMG6deu46aabnMesWbOGCRMmcN1117Fx40auu+46xo8fz6+//lpdb6PGdWkSDGgyYREREaliJpOja547lnKGpTZt2mAymdi2bVu5ji8tqJwazIrfahETE8P27dt544038PHx4fbbb2fAgAHk5eUREBDAH3/8wZw5c4iKiuLxxx8nLi6OlJQULr30UuLj451LUaibPHky8+bNIy0tjffff5/Y2FiGDBkCwMsvv8yrr77Kgw8+yIoVK4iPj2fYsGHk5uaW670Zpdyndup7W7t2LVdeeSUjRozgm2++YcOGDTzyyCPlfo3Sfkane82iLnnF99nt9gq91ples/j2qVOnsmXLFi6++GJWrFhBx44dmT9/PgA33XQTu3fv5rrrrmPz5s306NGD//znP5WqpbzcGq5GjBjB008/zWWXXVau49euXUvz5s25++67adGiBeeffz7/+Mc/nDetAUyfPp0LL7yQKVOm0L59e6ZMmcKQIUPOOB9AXaLJhEVERKShCg0NZdiwYbzxxhtkZmaW2J+SknLa8zt06MDPP//ssm316tV06NDB+dzHx4dLL72U119/nZUrV7JmzRpn64uHhwdDhw7lhRdeYNOmTezdu5cVK1YQEBBA69atnYuPjw8A48ePx2Kx8Omnn/LBBx9www03OIPBTz/9xOjRo7n22muJi4ujZcuW7Nixo9w/i44dO7J//34SE0+2Nq5Zs8blmF9++YXY2FgeeeQRevToQZs2bUqMYOjl5UVBQcEZXys+Pt7lZ/7LL79gNptp27ZtuWuuiKL3d+DAAee2rVu3kpqa6vJ5tW3bln/+858sWbKEyy67jPfff9+5LyYmhltvvZWvvvqKf/3rX7zzzjvVUmuROjVaYN++fTl48CDfffcdhmFw6NAhvvzySy6++GLnMWvWrOGiiy5yOW/YsGGsXr26zOvabDbS0tJcltqsS9GIgYWDWoiIiIg0JG+++SYFBQX06tWLefPmsWPHDrZt28brr79eare44h544AFmz57NW2+9xY4dO3jllVf46quvnPc+zZ49m/fee48///yT3bt389FHH+Hj40NsbCzffPMNr7/+OvHx8ezbt48PP/wQu91Ou3btynw9f39/JkyYwMMPP0xiYiKTJk1y7mvdujVLly5l9erVbNu2jX/84x8kJyeX++cwdOhQ2rVrx/XXX8/GjRv56aefeOSRR1yOad26Nfv37+ezzz5j165dvP76686WnSLNmzdnz549xMfHc/ToUWw2W4nXuuaaa/D29mbixIn8+eef/PDDD9x1111cd911zi6BlVVQUODS6hcfH8/WrVsZOnQoXbt25ZprruGPP/7gt99+4/rrr2fgwIH06NGD7Oxs7rzzTlauXMm+ffv45ZdfWLdunTN43XvvvSxevJg9e/bwxx9/sGLFCpdQVh3qXLj65JNPmDBhAl5eXkRGRhIcHOzSvJecnFziA46IiDjtL+q0adMICgpyLsX7ddZGbSMCsHqYSc/JZ++xLHeXIyIiIlKjWrRowR9//MHgwYP517/+RefOnbnwwgtZvnw5M2fOPO25Y8aM4bXXXuPFF1+kU6dO/Pe//+X9999n0KBBgGO0wXfeeYd+/frRtWtXli9fzv/+9z/CwsIIDg7mq6++4oILLqBDhw689dZbzJkzh06dOp32NSdPnsyJEycYOnQozZo1c25/7LHH6N69O8OGDWPQoEFERkaecfyB4sxmM/Pnz8dms9GrVy9uuukmnnnmGZdjRo8ezT//+U/uvPNOzjnnHFavXs1jjz3mcsy4ceMYPnw4gwcPJjw8vNTh4H19fVm8eDHHjx+nZ8+eXH755QwZMqTct/ecTkZGBt26dXNZRo4c6RwKPiQkhAEDBjB06FBatmzJ3LlzAbBYLBw7dozrr7+etm3bMn78eEaMGMGTTz4JOELbHXfcQYcOHRg+fDjt2rXjzTffPOt6T8dk1JKmD5PJdMYBLYoS7D//+U+GDRtGUlISDzzwAD179uS9994DHM2aH3zwAVdddZXzvE8++YTJkyeTk5NT6nWLbh4skpaWRkxMDKmpqRW+2a+mjH3zFzbsT+G1K89h9DlN3F2OiIiI1EE5OTns2bOHFi1a4O3t7e5yRNzmdN+FtLQ0goKCypUN6tRQ7NOmTaNfv3488MADAHTt2hU/Pz/69+/P008/TVRUFJGRkSVaqQ4fPnza5kqr1VrnZp/u2iSIDftT2HwwVeFKRERERKQWqFPdArOysjCbXUsuGuqxqAGuT58+JWamXrJkCX379q2ZImtI8cmERURERETE/dzacpWRkcHOnTudz4tupAsNDaVZs2ZMmTKFhIQEPvzwQwBGjRrFzTffzMyZM53dAu+991569epFdLRjNvF77rmHAQMG8PzzzzN69Gi+/vprli1bVmJUmLquaMTAPxNTKbAbWMzlG75URERERESqh1tbrtavX++8aQ0cM2R369aNxx9/HICkpCT279/vPH7SpEm88sorzJgxg86dO3PFFVfQrl07vvrqK+cxffv25bPPPuP999+na9euzJ49m7lz55Zrluu6pGW4P75eFrJyC9h9JMPd5YiIiIiINHi1ZkCL2qQiN6250/i31vDb3uO8dEUcl5/b9MwniIiIiBRTdBN/8+bNnfMyiTRE2dnZ7N2796wHtKhT91yJq65FkwkfTHFvISIiIlIneXp6Ao772kUastzcXODkeA6VVadGCxRXzsmEEzSohYiIiFScxWIhODiYw4cPA465jEwm3cctDYvdbufIkSP4+vri4XF28Ujhqg7rWjhi4NbENPIK7Hha1BApIiIiFRMZGQngDFgiDZHZbKZZs2Zn/Y8LCld1WPMwXwK8PUjPyefvQ+l0ig5yd0kiIiJSx5hMJqKiomjcuDF5eXnuLkfELby8vEpM+VQZCld1mMlkomvTIH7ZeYzNB1MVrkRERKTSLBbLWd9vItLQqR9ZHdelSTAAGzWZsIiIiIiIWylc1XFFkwlvTkhxbyEiIiIiIg2cwlUdVzRi4PbkdHLyCtxcjYiIiIhIw6VwVcc1CfYh1M+LvAKDv5LT3V2OiIiIiEiDpXBVxxUNagGaTFhERERExJ0UruqBrk0KJxPWoBYiIiIiIm6jcFUPdCmcTFjhSkRERETEfRSu6oGiboE7DqeTlZvv5mpERERERBomhat6ICLQm4hAK3YDtiamubscEREREZEGSeGqntBkwiIiIiIi7qVwVU/EacRAERERERG3UriqJ4omE96UoJYrERERERF3ULiqJ7oWjhi4+0gmaTl57i1GRERERKQBUriqJ0L9vGga4gPAn2q9EhERERGpcQpX9UhX531XClciIiIiIjVN4aoeKRoxUJMJi4iIiIjUPIWreiTOOahFinsLERERERFpgBSu6pFOTRzh6sDxbE5k5rq5GhERERGRhkXhqh4J8vGkRSM/QEOyi4iIiIjUNIWreqarJhMWEREREXELhat6pkth10ANaiEiIiIiUrMUruqZosmEFa5ERERERGqWwlU90yk6ELMJktNyOJyW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yKjCroHfbU7Cll/gjipFRETkDBSuRERqg8AoxxxY1iA4sBa+ugXsdufu3i3CiAi0kpaTz6q/j7qxUBERESmLwpWISG3RuANc+QlYvGDbQljyiHOXxWzikq6O1quv4xPcVaGIiIichsKViEht0qI/jJnpWF/7Jqx507lr9DmOcLVs2yEybfnuqE5EREROQ+FKRKS26XI5DH3Ssb74YdiywLG5SRDNw3zJybOzdOsh99UnIiIipVK4EhGpjfrdAz1vBgzH/Vf71mAymbj0nCaARg0UERGpjRSuRERqI5MJRjwP7S6GAht8dhUc3eGcUHjV30c4kZnr5iJFRESkOIUrEZHaymyBce9Ckx6QfQI+vozWPpl0ig4k327w3Z9J7q5QREREilG4EhGpzbx84eq5ENoSUvbDp+O5rFMwAAvj1TVQRESkNlG4EhGp7fwawTVfgm8YJMVzzYEnsFDAb3uPk5SafebzRUREpEYoXImI1AVhreCqueDhg/fe5fw35FMMw+CbjeoaKCIiUlsoXImI1BUxPR33YGFiaPb33GH5mq83akJhERGR2kLhSkSkLulwCYx8EYAHPD+nTdK37DqS4eaiREREBBSuRETqnl43Q9+7AXjB823iV853c0EiIiICClciInXT0Cc50GQEnqYChm95ACN5s7srEhERafAUrkRE6iKzmZCr3+NXowN+ZJP/4eWQqvuvRERE3EnhSkSkjvL38+OL1s/zt70JnlnJ8MnlkJPq7rJEREQaLIUrEZE67MLu7bgh90GOEgyHt8I3/3R3SSIiIg2WwpWISB02qF04ad5R3GwrDFVbF0J2iltrEhERaagUrkRE6jCrh4URnSPZYLThkLU52PNg+/fuLktERKRBUrgSEanjRp/TBID5tp6ODVsXuK8YERGRBkzhSkSkjjuvZRjhAVbmFYWrXSs0sIWIiIgbKFyJiNRxFrOJi7tEscNoSpJXLBTkqmugiIiIGyhciYjUA5ef2xSAz7POdWzYssB9xYiIiDRQClciIvVA5yZBXN8nlm8LegNg7FquroEiIiI1TOFKRKSeeHB4ezICWrPTHo2pIBe2L3J3SSIiIg2KwpWISD3hb/XgmXFd+dbuaL1KWf+5mysSERFpWBSuRETqkcHtGmNrcykAvgdWYss44d6CREREGhCFKxGReubmcRezhyZ4kc+Krz9wdzki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qjXzXNUmmudKRERERESgYtmgzt9zJSIiIiIiUhsoXImIiIiIiFQBhSsREREREZEqoHAlIiIiIiJSBRSuREREREREqoDClYiIiIiISBVQuBIREREREakCClciIiIiIiJVwMPdBdRGRfMqp6WlubkSERERERFxp6JMUJQRTkfhqhTp6ekAxMTEuLkSERERERGpDdLT0wkKCjrtMSajPBGsgbHb7SQmJhIQEIDJZDrtsWlpacTExHDgwAECAwNrqEKpafqc6z99xg2DPuf6T59xw6DPuf6rTZ+xYRikp6cTHR2N2Xz6u6rUclUKs9lM06ZNK3ROYGCg2z94qX76nOs/fcYNgz7n+k+fccOgz7n+qy2f8ZlarIpoQAsREREREZEqoHAlIiIiIiJSBRSuzpLVauWJJ57AarW6uxSpRvqc6z99xg2DPuf6T59xw6DPuf6rq5+xBrQQERERERGpAmq5EhERERERqQIKVyIiIiIiIlVA4UpERERERKQKKFyJiIiIiIhUAYWrs/Tmm2/SokULvL29Offcc/npp5/cXZJUkalTp2IymVyWyMhId5clZ2nVqlWMGjWK6OhoTCYTCxYscNlvGAZTp04lOjoaHx8fBg0axJYtW9xTrFTKmT7jSZMmlfhun3feee4pVipl2rRp9OzZk4CAABo3bsyYMWPYvn27yzH6Ltd95fmc9X2u22bOnEnXrl2dEwX36dOH77//3rm/Ln6PFa7Owty5c7n33nt55JFH2LBhA/3792fEiBHs37/f3aVJFenUqRNJSUnOZfPmze4uSc5SZmYmcXFxzJgxo9T9L7zwAq+88gozZsxg3bp1REZGcuGFF5Kenl7DlUplnekzBhg+fLjLd/u7776rwQrlbP3444/ccccdrF27lqVLl5Kfn89FF11EZmam8xh9l+u+8nzOoO9zXda0aVOee+451q9fz/r167ngggsYPXq0M0DVye+xIZXWq1cv49Zbb3XZ1r59e+Pf//63myqSqvTEE08YcXFx7i5DqhFgzJ8/3/ncbrcbkZGRxnPPPefclpOTYwQFBRlvvfWWGyqUs3XqZ2wYhjFx4kRj9OjRbqlHqsfhw4cNwPjxxx8Nw9B3ub469XM2DH2f66OQkBDj3XffrbPfY7VcVVJubi6///47F110kcv2iy66iNWrV7upKqlqO3bsIDo6mhYtWnDllVeye/dud5ck1WjPnj0kJye7fK+tVisDBw7U97qeWblyJY0bN6Zt27bcfPPNHD582N0lyVlITU0FIDQ0FNB3ub469XMuou9z/VBQUMBnn31GZmYmffr0qbPfY4WrSjp69CgFBQVERES4bI+IiCA5OdlNVUlV6t27Nx9++CGLFy/mnXfeITk5mb59+3Ls2DF3lybVpOi7q+91/TZixAg++eQTVqxYwcsvv8y6deu44IILsNls7i5NKsEwDO677z7OP/98OnfuDOi7XB+V9jmDvs/1webNm/H398dqtXLrrbcyf/58OnbsWGe/xx7uLqCuM5lMLs8NwyixTeqmESNGONe7dOlCnz59aNWqFR988AH33XefGyuT6qbvdf02YcIE53rnzp3p0aMHsbGxfPvtt1x22WVurEwq484772TTpk38/PPPJfbpu1x/lPU56/tc97Vr1474+HhSUlKYN28eEydO5Mcff3Tur2vfY7VcVVKjRo2wWCwlkvPhw4dLJGypH/z8/OjSpQs7duxwdylSTYpGg9T3umGJiooiNjZW3+066K677mLhwoX88MMPNG3a1Lld3+X6pazPuTT6Ptc9Xl5etG7dmh49ejBt2jTi4uJ47bXX6uz3WOGqkry8vDj33HNZunSpy/alS5fSt29fN1Ul1clms7Ft2zaioqLcXYpUkxYtWhAZGenyvc7NzeXHH3/U97oeO3bsGAcOHNB3uw4xDIM777yTr776ihUrVtCiRQuX/fou1w9n+pxLo+9z3WcYBjabrc5+j9Ut8Czcd999XHfddfTo0YM+ffrw9ttvs3//fm699VZ3lyZV4P7772fUqFE0a9aMw4cP8/TTT5OWlsbEiRPdXZqchYyMDHbu3Ol8vmfPHuLj4wkNDaVZs2bce++9PPvss7Rp04Y2bdrw7LPP4uvry9VXX+3GqqUiTvcZh4aGMnXqVMaNG0dUVBR79+7l4YcfplGjRowdO9aNVUtF3HHHHXz66ad8/fXXBAQEOP9lOygoCB8fH0wmk77L9cCZPueMjAx9n+u4hx9+mBEjRhATE0N6ejqfffYZK1euZNGiRXX3e+y2cQrriTfeeMOIjY01vLy8jO7du7sMDyp124QJE4yoqCjD09PTiI6ONi677DJjy5Yt7i5LztIPP/xgACWWiRMnGobhGML5iSeeMCIjIw2r1WoMGDDA2Lx5s3uLlgo53WeclZVlXHTRRUZ4eLjh6elpNGvWzJg4caKxf/9+d5ctFVDa5wsY77//vvMYfZfrvjN9zvo+13033nij8+/o8PBwY8iQIcaSJUuc++vi99hkGIZRk2FORERERESkPtI9VyIiIiIiIlVA4UpERERERKQKKFyJiIiIiIhUAYUrERERERGRKqBwJSIiIiIiUgUUrkRERERERKqAwpWIiIiIiEgVULgSERERERGpAgpXIiIiVcxkMrFgwQJ3lyEiIjVM4UpEROqVSZMmYTKZSizDhw93d2kiIlLPebi7ABERkao2fPhw3n//fZdtVqvVTdWIiEhDoZYrERGpd6xWK5GRkS5LSEgI4OiyN3PmTEaMGIGPjw8tWrTgiy++cDl/8+bNXHDBBfj4+BAWFsYtt9xCRkaGyzGzZs2iU6dOWK1WoqKiuPPOO132Hz16lLFjx+Lr60ubNm1YuHBh9b5pERFxO4UrERFpcB577DHGjRvHxo0bufbaa7nqqqvYtm0bAFlZWQwfPpyQkBDWrVvHF198wbJly1zC08yZM7njjju45ZZb2Lx5MwsXLqR169Yur/Hkk08yfvx4Nm3axMiRI7nmmms4fvx4jb5PERGpWSbDMAx3FyEiIlJVJk2axMcff4y3t7fL9oceeojHHnsMk8nErbfeysyZM537zjvvPLp3786bb77JO++8w0MPPcSBAwfw8/MD4LvvvmPUqFEkJiYSERFBkyZNuOGGG3j66adLrcFkMvHoo4/y1FNPAZCZmUlAQADfffed7v0SEanHdM+ViIjUO4MHD3YJTwChoaHO9T59+rjs69OnD/Hx8QBs27aNuLg4Z7AC6NevH3a7ne3bt2MymUhMTGTIkCGnraFr167OdT8/PwICAjh8+HBl35KIiNQBClciIlLv+Pn5leimdyYmkwkAwzCc66Ud4+PjU67reXp6ljjXbrdXqCYREalbdM+ViIg0OGvXri3xvH379gB07NiR+Ph4MjMznft/+eUXzGYzbdu2JSAggObNm7N8+fIarVlERGo/tVyJiEi9Y7PZSE5Odtnm4eFBo0aNAPjiiy/o0aMH559/Pp988gm//fYb7733HgDXXHMNTzzxBBMnTmTq1KkcOXKEu+66i+uuu46IiAgApk6dyq233krjxo0ZMWIE6enp/PLLL9x11101+0ZFRKRWUbgSEZF6Z9GiRURFRblsa9euHX/99RfgGMnvs88+4/bbbycyMpJPPvmEjh07AuDr68vixYu555576NmzJ76+vowbN45XXnnFea2JEyeSk5PDq6++yv3330+jRo24/PLLa+4NiohIraTRAkVEpEExmUzMnz+fMWPGuLsUERGpZ3TPlYiIiIiISBVQuBIREREREakCuudKREQaFPWGFxGR6qKWKxERERERkSqgcCUiIiIiIlIFFK5ERERERESqgMKViIiIiIhIFVC4EhERERERqQIKVyIiIiIiIlVA4UpERERERKQKKFyJiIiIiIj8/6gAAC4ST9xEJ/v+AAAAAElFTkSuQmCC",
      "text/plain": [
       "<Figure size 1000x500 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# 计算和打印训练集和交叉验证集的准确率\n",
    "FNNmodel.eval()\n",
    "with torch.no_grad():\n",
    "   # 训练集准确率\n",
    "    TrainCorrect = 0\n",
    "    TrainTotal = 0\n",
    "    for images, labels in TrainLoader:\n",
    "        outputs = FNNmodel(images)\n",
    "        _, predicted = torch.max(outputs, 1)\n",
    "        _, true_labels = torch.max(labels, 1)\n",
    "        TrainTotal += labels.size(0)\n",
    "        TrainCorrect += (predicted == true_labels).sum().item()\n",
    "    \n",
    "    TrainAccuracy = 100 * TrainCorrect / TrainTotal\n",
    "    \n",
    "   # 交叉验证集准确性\n",
    "    CVCorrect = 0\n",
    "    CVTotal = 0\n",
    "    for images, labels in cvLoader:\n",
    "        outputs = FNNmodel(images)\n",
    "        _, predicted = torch.max(outputs, 1)\n",
    "        _, true_labels = torch.max(labels, 1)\n",
    "        CVTotal += labels.size(0)\n",
    "        CVCorrect += (predicted == true_labels).sum().item()\n",
    "    \n",
    "    CVAccuracy = 100 * CVCorrect / CVTotal\n",
    "\n",
    "print(f'Accuracy on training set: {TrainAccuracy:.2f}%')\n",
    "print(f'Accuracy on cross-validation set: {CVAccuracy:.2f}%')\n",
    "\n",
    "# 绘制训练和交叉验证损失\n",
    "plt.figure(figsize=(10, 5))\n",
    "plt.plot(range(1, num_epochs+1), TrainLosses, label='Training Loss')\n",
    "plt.plot(range(1, num_epochs+1), CVLosses, label='Cross-Validation Loss')\n",
    "plt.xlabel('Epoch')\n",
    "plt.ylabel('Loss')\n",
    "plt.title('Training and Cross-Validation Loss')\n",
    "plt.legend()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ce1b38e6-41f2-4b92-9610-194b538ac385",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
